Skip to content

Instantly share code, notes, and snippets.

@crutchcorn
Last active January 8, 2022 08:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crutchcorn/75eb07fb3a6a7dbc478678579ab5d84d to your computer and use it in GitHub Desktop.
Save crutchcorn/75eb07fb3a6a7dbc478678579ab5d84d to your computer and use it in GitHub Desktop.

Before we can dive into how many front-end frameworks that you may have heard of work, we need to set a baseline of information. If you're already familiar with how the DOM represents a tree and how the browser takes that information and utilizes it, great! You're ready to read ahead! Otherwise, it's strongly suggested that you take a look at our post introducing the concepts required to understanding some of the baseline to this post

You may have heard about various frameworks and libraries that modern front-end developers utilize to build large-scale applications. Some of these frameworks you may have heard of are Angular, React, and Vue. While each of these libraries bring their own strengths and weaknesses, many of the core concepts are shared between them.

With this series of articles, we're going to be outlining core concepts that are shared between them and how you can implement them in code in all three of the frameworks. This should provide a good reference when trying to learn one of these frameworks without a pre-requisite knowledge or even trying to learn another framework with some pre-requisite of a different one.

Components

Let's first explain why frameworks like Angular, React, or Vue differ from other libraries that may have come before it, like jQuery.

It all comes down to a single core concept at the heart of each of them: Componentization.

The idea of a component is that you have a modular system of HTML, JS, and CSS comprised of individual "components" that you them compose to make up a larger UI. For example, instead of one HTML file that contains:

  • Header
  • Sidenav
  • Form logic
  • Footer

You might have a component for each of them and end up with a component to roll them all into a single page like this:

<div>
  <header></header>
  <sidenav></sidenav>
  <form-logic></form-logic>
  <footer></footer>
</div>

As you'll notice, the layout of these "components" is very similar to the way we structure a DOM tree

Virtual DOM

Please note that "Virtual DOM" is somewhat of a catch-all term (and a devisive one at that). There is no standard definition of what a virtual DOM is or what it looks like, even, and there is huge contestions between whether we should standardize that concept at all or not

One of the main advan

Inputs

Outputs

Fragments

One of the advantages that having a virtual representation of the DOM is that you're able to have a non 1:1 layout of virtual node to real DOM node. What does this mean? Consider a scenario like this: let's say that you have various CDNs for different media types to place into a video tag. However, instead of hardcoding the CDN delivery every time, you want to end up with a single video name and a component that pre/post-pends the rest of the URL to the correct source location

---------- Angular --------

<video>
  <custom-html-video-sources [videoName]="'best-video-ever'"></custom-html-video-sources>
</video>

---------- React --------

<video>
  <custom-html-video-sources videoName={'best-video-ever'}/>
</video>

---------- Vue --------

<video>
  <custom-html-video-sources :videoName="'best-video-ever'"></custom-html-video-sources>
</video>

In this example, we may want to have the video tag and seperate out the source tags. Something like this might apply:

---------- Angular --------

@Component({
	selector: "custom-html-video-sources",
	template: `
		<ng-container>
			<source [src]="'https://cdn.example.com/mp4/' + videoName + '.mp4'"/>
			<source [src]="'https://cdn.example.com/mp4/' + videoName + '.mp4'"/>
		</ng-container>
	`
})
class CustomHTMLVideoComponent {
	@Input() videoName: string;
}

---------- React --------

<custom-html-video videoTypes={[
  {
  	type: 'mp4',
  	src: 'video.mp4'
  },
  {
  	type: 'mkv'.
  	src: 'video.mkv'
  }
]}/>

---------- Vue --------

<custom-html-video :videoTypes="[
  {
  	type: 'mp4',
  	src: 'video.mp4'
  },
  {
  	type: 'mkv'.
  	src: 'video.mkv'
  }
]"/>

Content Projection

---------- Angular --------


---------- React --------

const Component = ({children}) => {
	<>
		{children}
	</>
}

---------- Vue --------


Named Scopes

Similar to how content projection works by displaying children of a component where present, you're also able to do this for more than a single set of children by providing what's called a "named slot" in order to project various peices of UI in more than a single location. For example, you could have an AppLayout component that would provide a place to insert a sidenav bar, main set of content, and more. This would allow you to project dynamic content where expected while keeping a consistent UI layout for the primary elements of your app

---------- Angular --------

@Component({
	selector: "app-layout",
	template: `
		<ng-container>
			<ng-content></ng-content>
		</ng-container>
	`
})
class AppLayoutComponent {
}

---------- React -------- Because a JSX tag simply returns a value as if it were any other function, you're able to utilize the props inside of a React component in order to pass those values and display them

const AppLayout = ({sidebar, mainContent}) => {
	<div>
		{sidebar}
		<header>{content}</header>
		<main>
			{mainContent}
    </main>
	</div>
}

const App = () => {
	return <AppLayout
		sidebar={<Sidebar/>}
		mainContent={<AppContent/>}
	/>
}

---------- Vue --------


Lifecycle methods

If you've used vanilla JavaScript for a project for an extended period of time, you may be familiar with document.onReady in order to load some code opperations when the DOM is fully loaded.

Event Listeners

Various frameworks even build upon the system the DOM itself has for raising state. Oftentimes, you're able to raise state by using events.

If you've used a Google Maps API before, you may be familiar with this pattern.

Portals

There may be times where you might want to render a component from one part of your virtual DOM to an entirely different place in the browser DOM. A good usecase for something like this might be something like a modal that you want to render at the root of the DOM in order to avoid z-indexing.

However, this modal needs to contain properties from part of your component system and emit a response back to that same part of the virtual DOM.

---------- React --------

Consider an example where you might have a dialog-display component with an onAccept property, an onReject property and a message property:

<div>
	<header></header>
	<main>
		<section>
			{shouldShow && 
				<dialog-display
          message="Licence agreement, do you accept?"
          onReject={handleClode} 
          onAccept={saveResponse}
				/>
      }
      <p>Hello there!</p>
		</section>
	</main>
</div>

---------- Angular -------- Consider an example where you might have a dialog-display component with an accept event emitter, a reject event emitter and a message property:

<div>
	<header></header>
	<main>
		<section>
        <dialog-display
          *ngIf="shouldShow"
          [message]="'Licence agreement, do you accept?'"
          (reject)="handleClode($event)"
          (accept)="saveResponse($event)"
        >
        </dialog-display>
        <p>Hello there!</p>
		</section>
	</main>
</div>

---------- Vue -------- Consider an example where you might have a dialog-display component with an accept event emitter, a reject event emitter and a message property:

<div>
	<header></header>
	<main>
		<section>
        <dialog-display
          v-if="shouldShow"
          :message="'Licence agreement, do you accept?'"
          @reject="handleClode($event)"
          @accept)="saveResponse($event)"
        >
        </dialog-display>
        <p>Hello there!</p>
		</section>
	</main>
</div>

But to have the following display in the DOM, when the component renders:

<html>
	<body>
    <div class="display-dialog">
      <p>Licence agreement, do you accept?</p>
      <button>Accept</button>
      <button>Reject</button>
    </div>
    <div>
      <header></header>
      <main>
        <section>
          <p>Hello there!</p>
        </section>
      </main>
    </div>
  </body>
</html>

In this example, when the user selects accept or reject, it passes the values "up" to the section, despite not being a child in the browser DOM due to it's placement in the virtual DOM. Data passed in also shows up as-expected, again, despite not being a child of that element in the browser.

Depending on implementation, even DOM events (clicking, focus events, and others) are even passed to the respective component section rather than to the parent body tag

Angular React Vue Notes
Portals* Portals No Official API * Angular CDK is what contains the Portal logic rather than built into Angular. It interacts differently with event bubbling/etc than how React (which is built in and uses VirtualDOM instead of template injection) works

Side Effects

Composing

A Note About Standards

Please do note that while there is web components, they may not serve the same functional purpose. Some frameworks you may be aware of oftentimes even play nicely with web components

https://custom-elements-everywhere.com/

Similarities:

Angular React Vue Notes
{{}} {}* {{}} * JSX (React) handles this a bit differently than the others. While you can use some JS in Vue and Angular, you can run all JavaScript (JS) within JSX
[prop] prop={} v-bind:prop=""/:prop=""
(event) Functions passed via props v-on:event/@event
OnChanges componentDidUpdate*/useEffect watch/vm.$watch**/watchEffect *This is a bit different. This is called when a render is called. This is because of the differences between local state and not with React/others
** This only listens to a single properties and not a list of others
constructor/ OnInit constructor/ComponentDidMount/useEffect(()=>{}, []); created/mounted/onMounted
OnDestroy ComponentWillUnmount/Return useEffect beforeDestroy/onBeforeDestroy
*ngFor map with key in JSX v-for with key
*ngIf Ternary operator with a null/undefined to prevent rendering v-if
OnChanges preveousValue diff checking/ChangeDetection.OnPush* ShouldComponentUpdate**/PureComponent/React.memo/React Forget * ChangeDetection.OnPush does NOT directly do the same thing as PureComponent as it pertains to a very specific Angular-only logic. That being said, if you're familiar with OnPush. ** In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.
ChangeDetectorRef* forceUpdate vm.$forceUpdate()** * This does not guarantee that the component will re-render - see back to differences between Angular and React
**Does not apply to children
[attr.atrib]="val" atrib={val} v-bind:atrib="val"/:atrib="val"
([ngModel]) Separate value and setter function props v-model
@ViewChild/elementRef.nativeElement React.createRef/useRef vm.$el
Pipes useMemo computed* * While Vue computed fields have a similar caching ability to pure Angular pipes, they can also have setters rather than just being used as a data pipe
[ngClass]="{'active': isActive}" No special API - className={} as with any other prop v-bind:class="{ active: isActive }"
@ViewChild React.Children ref/$vm.refs
ngContent this.props.children slot
event.emit(value) Child function pass* $emit('event', val) *This goes much more inline with the raise state logic that React pushes very hard - and is probably the main reason for this
ng-container Fragments Vue 2: BYOF - Bring your own fragments
Vue 3: <template>
Class props state = {}/useState data/useRef/reactive
Angular Universal/ScullyIO* Next.JS*/Gatsby* Nuxt.JS* * These are unofficial solutions but are the most popular versions of these concepts
Output(?). Classes sorta handle this for free forwardRef expose

Differences:

Angular React Vue
Change Detection Zones (ZoneJS) Maniually State Calling (setState) getters and setters (Object.definedProperty v2/Proxy v3)

angular/angular#22587

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment