Vanilla JS - You Might Not Need a Framework
Vanilla JS - You Might Not Need a Framework
Introduction
What we’ll cover > 我们将会学习什么
- Vanilla JS > 原生 JS
- DOM API
- Fetch
- Design Patterns > 设计模式
- Single page applications > 单页应用
- Web Components
- Reactive programming > 响应式编程
- Routing > 路由
Pre-requisites > 先决条件
Vanilla JS
What is Vanilla JavaScript
Help you avoid overusing frameworks like React, Vue, Angular, etc. > 帮助你避免过度使用 React、Vue、Angular 等框架
- When you have a hammer, everything looks like a nail. > 当你有了一把锤子,所有东西看起来都像钉子
Vanilla JavaScript > 原生 JavaScript
- The usage of the core language and browser APIs to create web apps without any additional libraries or frameworks added on the top. > 使用核心语言和浏览器 API 来创建 web 应用,而不需要在顶部添加任何其他库或框架
Vanilla comes from Ice Cream, meaning plain or without any extras. > Vanilla 来自冰淇淋,意思是普通的或没有任何额外的东西
Why Vanilla JavaScript
Why do we need to care about VanillaJS? > 为什么我们需要关心 VanillaJS?
- Add on more tool to your toolbox. > 给你的工具箱添加更多的工具
- Understand what your library is doing. > 了解你的库在做什么
- Extend your library with plugins. > 使用插件扩展你的库
- To be a better web developer. > 成为更好的 web 开发者
- To mix with libraries. > 与库混合使用
- To use it! > 使用它!
- You can create simple and fast web apps with no CLI, no build process. > 您可以创建简单快速的 web 应用程序,无需 CLI,无需构建过程
Main advantages of Vanilla JS > 原生 JS 的主要优势
- Lightweight > 轻量
- Control and Power > 控制和能力
- Simplicity > 简单
- Flexibility > 灵活性
- Performance > 性能
- No node_modules > 没有 node_modules
Main fears of VanillaJS > 原生 JS 的主要担忧
- Routing for single page applications > 单页应用的路由
- Too verbose and time consuming > 太啰嗦和耗时
- State management > 状态管理
- Template > 模板
- Complexity > 复杂性
- Reusable Components > 可复用组件
- Maintenance > 可维护性
- Learning curve > 学习曲线
- Browser compatibility > 浏览器兼容性
- Reinventing the wheel every time > 重复造轮子
- Scalability > 可扩展性
Learn the tool and use it when it’s the best options. > 学习和使用工具,当它是最好的选择时
We won’t advocate for using Vanilla JS on every project. > 我们不会主张在每个项目中都使用原生 JS
The DOM API
- In this workshop, we’ll be using different techniques and design patterns in parallel. > 在这个工作坊中,我们将同时使用不同的技术和设计模式
DOM Essentials
The Document Object Modal connects web pages to JavaScript by representing the structure of a document in memory. > 文档对象模型通过在内存中表示文档的结构来将 web 页面连接到 JavaScript
DOM API
- A browser API exposed to developers to manipulate the DOM from an scripting language. > 暴露给开发人员的浏览器 API,用于使用脚本语言操作 DOM
The DOM API is available on many objects > DOM API 可用于许多对象上
window
global objectdocument
object- One object per HTML element and other nodes in your document
Each element is represented by an object of HTMLElement interface or other interface that inherit it. > 每个元素由 HTMLElement 接口的对象或继承它的其他接口表示
They have instance properties and methods. > 它们有实例属性和方法
Changes in properties or children will trigger updates in the user interface when you release the thread. > 属性或子元素的更改将在释放线程时触发用户界面的更新
We can listen to events happening in that element and react in consequence. > 我们可以监听发生在该元素中的事件,并做出相应的反应
To work with DOM elements, we can > 要使用 DOM 元素,我们可以
- Pick them from the current DOM > 从当前 DOM 中选择它们
- Create them, and then inject them into the DOM > 创建它们,然后将它们注入到 DOM 中
When we have a reference to an Element, we can > 当我们持有一个元素的引用时,我们可以
- Read its content > 读取它的内容
- Change its content > 改变它的内容
- Remove it > 删除它
- Add new elements into it > 向其中添加新元素
Finding Elements in the DOM
By id -
document.getElementById('id')
By class name -
document.getElementsByClassName('class')
By name -
document.getElementsByName('name')
By CSS selector -
document.querySelector('selector')
Navigating DOM structure -
element.parentElement
…When selecting elements, some functions return…
- One HTML Element(HTMLElement) > 一个 HTML 元素
- A live HTML Collection(HTMLCollection) > 一个活动的 HTML 集合
- Static Element collection(NodeList) > 静态元素集合
When you use a function that returns one element, it can also return null if no node was found. > 当你使用返回一个元素的函数时,如果没有找到节点,它也可以返回 null
When you use a function that returns multiple elements, it can also return an empty collection if no elements were found. > 当你使用返回多个元素的函数时,如果没有找到元素,它也可以返回一个空集合
HTMLCollection
(live) don’t have all the modern Array interface, such asfilter
,map
,reduce
, etc. >HTMLCollection
(live) 没有所有现代数组接口,如filter
、map
、reduce
等NodeList
(static) have all the modern Array interface, such asfilter
,map
,reduce
, etc. >NodeList
(static) 有所有现代数组接口,如filter
、map
、reduce
等With an HTML element in JavaScript, you can > 用 JavaScript 中的 HTML 元素,你可以
- Read and change attributes’ values > 读取和更改属性的值
- Read and change styles > 读取和更改样式
- Hook event listeners > 挂载事件监听器
- Add, remove or move children > 添加、删除或移动子元素
- Read and change content > 读取和更改内容
- More…
Modifying DOM
How to read or change attributes of a DOM element > 如何读取或更改 DOM 元素的属性
- You can use dot syntax to access properties mapped from HTML attributes. > 你可以使用点语法来访问从 HTML 属性映射的属性
- Most of the time, the property name is the same as the attribute name. > 大多数情况下,属性名与属性名相同
- Some properties are different, such as
class
andclassName
. > 有些属性是不同的,如class
和className
- You can use dot syntax to access a style object. > 你可以使用点语法来访问样式对象
- That object will have a map to every CSS property you can inline in HTML. > 该对象将有一个映射到你可以在 HTML 中内联的每个 CSS 属性
- You can use dot syntax and bind a function to an event name using
addEventListener
. There is a list of standard events. > 你可以使用点语法和addEventListener
将函数绑定到事件名。有一个标准事件列表
- You can use dot syntax to access properties mapped from HTML attributes. > 你可以使用点语法来访问从 HTML 属性映射的属性
Accessing and Editing Contents of Elements > 访问和编辑元素内容
- Accessing
textContent
> 访问textContent
- Accessing
innerHTML
> 访问innerHTML
- Using DOM APIs to create new nodes > 使用 DOM API 创建新节点
- Accessing
The DOM
Course Project Overview
Our Project
- APP: Coffee Master
- Web app for a coffee shop
- Menu of products
- Details for each product
- Order with Cart
- HTML and CSS provided
- Data in JSON
- No JavaScript written yet
HTML and DOM are not same thing exactly. > HTML 和 DOM 不是完全相同的东西
- HTML is a markup language, plain text. > HTML 是一种标记语言,纯文本
- DOM is a representation of the HTML document in memory. > DOM 是 HTML 文档在内存中的表示
HTML parser will auto insert the first visible element into the
document.body
> HTML 解析器将自动将第一个可见元素插入到document.body
中
1 | <script></script> |
Scoping querySelector
1 | // Search in the whole document |
Adding Scripts Async & Defer
- Old school - put script tag in the bottom of body > 将 script 标签放在 body 底部
1 | <body> |
- Async - load script asynchronously > 异步加载脚本
1 | <head> |
- Defer - load script after HTML parsing > 在 HTML 解析后加载脚本
1 | <body> |
- If you are not sure, use defer. > 如果你不确定,使用 defer
- Async will execute the script as soon as it is downloaded. It will block the HTML parsing. > 异步将在下载脚本后立即执行脚本。它将阻塞 HTML 解析
- Defer will execute the script after the HTML parsing is done. > Defer 将在 HTML 解析完成后执行脚本
Main Script Setup
1 | window.addEventListener('DOMContentLoaded', () => { |
Event Binding & Handlers
Basic: load, click, dbclick
Value: change
Keyboard: keyup, keydown, keypress
Mouse: mouseup, mousedown, mousemove, mouseenter, mouseleave
Pointer and Touch: pointerup, pointerdown, pointermove, pointerenter, pointerleave, touchstart, touchend, touchmove
Focus: focus, blur, focusin, focusout
Scroll: scroll
Special: DOMContentLoaded, popstate in window, submit in form, resize in window, hashchange in window
The spec’s naming pattern is to use lowercase with no word separator. > 规范的命名模式是使用小写字母而没有单词分隔符
webkitcurrentplaybacktargetiswirelesschanged
Two way to bind event handlers > 两种绑定事件处理程序的方法
- Inline like
element.onclick = () => {}
- Using
addEventListener
> 使用addEventListener
- Inline like
addEventListener
can bind multiple handlers to the same event. >addEventListener
可以将多个处理程序绑定到同一事件Inline event handlers can only bind one handler to the same event. > 内联事件处理程序只能将一个处理程序绑定到同一事件
Advanced Event Handling
- Event Options > 事件选项
capture
- the event will be captured on the way down the DOM tree. > 事件将在 DOM 树下行时捕获once
- the event will only be fired once. > 事件只会触发一次passive
- the event will not callpreventDefault
, so it will not block the browser from doing its default behavior. Better not change the DOM in this handler. > 事件不会调用preventDefault
,因此它不会阻止浏览器执行其默认行为。最好不要在此处理程序中更改 DOM
- Dispatch Custom Events > 分派自定义事件
new Event('name')
element.dispatchEvent(event)
Helpful Utils
1 | const $ = (...args) => document.querySelector.call(this, ...args); |
Routing
Browser Routing & History API
SPA: How to change content > 如何更改内容
- Remove Previous page and inject new page into the DOM > 删除上一页并将新页注入到 DOM 中
- Hide Previous page and show new page > 隐藏上一页并显示新页
Single page application and router > 单页应用和路由
- We want to change the content of the page based on what user select: > 我们想根据用户的选择更改页面的内容
- Homepage: menu
- Detail page: product detail
- Order: cart and order form
- We won’t have multiple HTML file, we will use the DOM APIs and web components. > 我们不会有多个 HTML 文件,我们将使用 DOM API 和 web 组件
- We will use the history API to push the new entries to the navigation history. > 我们将使用 history API 将新条目推送到导航历史记录中
- We want to change the content of the page based on what user select: > 我们想根据用户的选择更改页面的内容
1 | history.pushState(state, title, url); |
Note: popstate won’t be fired if the user clicks on an external link or change the URL manually. > 注意:如果用户单击外部链接或手动更改 URL,则不会触发 popstate
If you are creating a SPA, configure your server properly and check the URL when the whole page loads for the first time. > 如果您正在创建 SPA,请正确配置服务器并在第一次加载整个页面时检查 URL
Make the router reuseable by receiving a collection of routes(path as a regex and component to render). > 通过接收路由集合(路径作为正则表达式和要渲染的组件)使路由器可重用
Web Components
Overview & Custom Elements
Web Component
- A modular, reuseable building block for web development that encapsulates a set of related functionality and user interface elements. > 一种模块化、可重用的 web 开发构建块,它封装了一组相关的功能和用户界面元素
- Compatible with every browser. > 与每个浏览器兼容
- It’s actually a set of standards. > 它实际上是一组标准
- Custom Elements > 自定义元素
- HTML Templates > HTML 模板
- Shadow DOM > Shadow DOM
- Declarative Shadow DOM > 声明式 Shadow DOM
- It’s similar to the idea of components on most of the library for JavaScript. > 它类似于大多数 JavaScript 库的组件思想
- We have freedom of choice on how to define them and use them. > 我们可以自由选择如何定义它们和使用它们
Custom Elements
A way to define new, reuseable HTML elements with custom behavior and functionality using JavaScript. > 使用 JavaScript 定义具有自定义行为和功能的新的可重用 HTML 元素的方法
1 | class MyElement extends HTMLElement { |
1 | <my-element></my-element> |
1 | class MyElement extends HTMLElement { |
HTML Template
Fragments of markup that can be cloned and inserted into the document at runtime, with reuseable HTML content that can be rendered and modified dynamically. > 可以在运行时克隆并插入到文档中的标记片段,具有可重用的 HTML 内容,可以动态地渲染和修改
1 | <template id="my-template"> |
1 | const template = document.getElementById('my-template'); |
By default, the nodes of our custom element are part of the same page’s DOM, so CSS style declaration applies to all the document. > 默认情况下,我们自定义元素的节点是同一页面 DOM 的一部分,因此 CSS 样式声明适用于整个文档
Shadow DOM
A private, isolated DOM tree within a web component that is separate from the main document’s DOM tree. > 一个私有的、与主文档 DOM 树分离的 web 组件内部的隔离 DOM 树
Allows more control over styling and encapsulation of functionality of a Custom Element. > 允许更多地控制自定义元素的样式和功能的封装
By default, CSS declared in the main DOM won’t be applied to the Shadow DOM. > 默认情况下,主 DOM 中声明的 CSS 不会应用于 Shadow DOM
There are new pseudo-classed and pseudo-element to allow communication between DOMs in stylesheets. > 有新的伪类和伪元素来允许样式表中的 DOM 之间的通信
It can be opened or closed defining visibility from the outer DOM. > 它可以通过从外部 DOM 定义可见性来打开或关闭
1 | class MyElement extends HTMLElement { |
Where to define HTML for a custom element > 在哪里定义自定义元素的 HTML
- There are several alternatives > 有几种选择
- Use DOM APIs > 使用 DOM API
- Use a
<template>
tag in the main HTML > 在主 HTML 中使用<template>
标签 - Use an external HTML file loaded with fetch(it can be prefetched) > 使用 fetch 加载的外部 HTML 文件(它可以被预取)
- Using
innerHTML
> 使用innerHTML
- Using DOM parser > 使用 DOM 解析器
- Using
Declarative Shadow DOM
A way to define shadow DOM directly in HTML markup using a new set of attributes and tags. > 一种使用一组新的属性和标签直接在 HTML 标记中定义 shadow DOM 的方法
- Where to define CSS for a custom element > 在哪里定义自定义元素的 CSS
- There are several alternatives > 有几种选择
- Use CSSOM APIs > 使用 CSSOM API
- Add a
<style>
tag to a<template>
. > 将<style>
标签添加到<template>
中 - Add a
<link>
in the<template>
. > 在<template>
中添加<link>
- Use an external CSS file loaded with fetch(it can be prefetched) and injected in the shadow DOM as a
<style>
. > 使用 fetch 加载的外部 CSS 文件(它可以被预取)并将其作为<style>
注入到 shadow DOM 中
Reactive Programming with Proxies
Creating a Proxy
- Proxy
- A wrapper object that lets you intercept and modify operations performed on the wrapped object, allowing you to add custom behavior or validation to the object’s properties and methods. > 一个包装对象,它允许你拦截和修改对包装对象执行的操作,从而允许你向对象的属性和方法添加自定义行为或验证
1 | const original = { |
Proxies work with objects only. If you want to do something similar with primitives, you can use classes with getter and setters. > 代理只适用于对象。如果你想用原始值做类似的事情,你可以使用带有 getter 和 setter 的类
- Proxy handler
- Object that contains traps for intercepting and customizing operations performed on a JavaScript proxy object. > 包含用于拦截和自定义对 JavaScript 代理对象执行的操作的陷阱的对象
- Proxy trap
- Method on a proxy handler object that intercepts and customizes a specific operation performed on the target object. > 代理处理程序对象上的方法,用于拦截和自定义对目标对象执行的特定操作