Tumgik
#vue 3 composition api props
meh
HTML 1. Layout html * article * aside * footer * header * nav * section * Div * Details and Summary * Figure * Paragraph * Span * time 2. Elementos html * br * Favicon * Headings * Inputs * Links * Listas * Table * Audio * Embed * Graphics * iFrame * Imagenes * Object * Video 3. Formatos de texto Bootstrap * Agregar bootstrap a html * Containers * Grid bs * Colors bootstrap | Colors * Imagenes bootstrap * Texto bootstrap * Tables bootstrap * Buttons bs * Alerts * Dropdown CSS 1. Basics 🐣 * Selectores * Background * Border * Paddings * Outline * Margins * Box sizing * Box reflect * Objects css * Columns * Cursores * Height and Width * Icons * FontAwesomeIcons * Google Icons * Inputs css * Links css * List * Tables * Text and Fonts * Resizing 2. Inter 🐥 * Content * Counters * Box shadow * Display and visibility * Flexbox * Grid * Float css | Float * Clear * Math * Overflow * Position * Z-index * @media queries * Variables css 3. Advanced 🐥 * Animations * Gradientes * Mask * 2D transforms * 3D transforms * Transitions JSS >* [[Agregar a html]] >* [[Comments in javascript]] >* [[Variables]] > * [[Numbers]] > * [[Boolean]] > * [[Strings]] > * [[Arrays]] >* [[Objects]] >* [[Class]] >* [[Operadores]] >* [[Functions]] >* [[Conditionals]] >* [[Ciclos]] >* [[JS Functions]] >* [[Exportar e importar]] >* [[Promise]] >* [[async await]] >* [[Regular expressions]] >* [[Lectura de Json]] >* [[Testing with Jest]] >* [[Api with Express]] > * [[body-parser]] > * [[nodemon]] > * [[mongoose]] > * [[dotenv]] >* [[Corrección eslint]] >* [[Constructor]] >* [[Principles for functional programming]] >* [[JavaScript Array Prototype]] >* [[Currying and Partial Application]] >* [[HTML DOM Methods]] > * [[DOM functions]] >* [[DOM Events]] > * [[DOM Event Listener]] >* [[Create new Elements]] VUE Aquí está el índice del código: >* [[Instalación]] >* [[Directivas]] >* [[script parts]] > * * [[Importar y exportar componentes]] > * Paso de valores > * [[Slot y Props]] > * [[emit]] >* [[filters]] >* [[router]] >* [[Store]] FRONTEND - Introducción - Cómo funciona el internet - WebServers, websites, webpages y Web Apps - Web browser - Web hosting - Single Page Application - Core Internet Technologies - APIS and Services - Protocolos de Internet - HTML - Teoría - Web Components - Elementos - Imagenes - Links - Table - Form - DOM - ¿Qué es DOM? - Shadow DOM - Virtual DOM - Event Listeners - CSS - Responsive Design - Flexbox - Grid - @media queries - Bootstrap - Procesadores CSS - SASS - Javascript - Elementos - Js Modules - POO Classes - Types of Events Js - Exportar e importar - Try and Catch - Fetch - Typescript - Decorators - Package manager - Dependencies - Package manager - Task Runners - Module Bundlers - Webpack - Frameworks and Libraries - Angular - Interpolación - Importar componentes - React - React Comandos Básicos - Estructura React - Assests React - Componentes React - Elements - Composition children props - Componetes especializados - HIgher Order Components - Render Props - JSX - State data y Hooks - useReducer - useRef - custom hooks - Eventos sintéticos - Navegación Router - Keys - Side Effects - Formularios - Controlled y uncontrolled components - React.memo - React.cloneElement y React.child - Testing with React
1 note · View note
suzanneshannon · 4 years
Text
A Font-Like SVG Icon System for Vue
Managing a custom collection of icons in a Vue app can be challenging at times. An icon font is easy to use, but for customization, you have to rely on third-party font generators, and merge conflicts can be painful to resolve since fonts are binary files.
Using SVG files instead can eliminate those pain points, but how can we ensure they’re just as easy to use while also making it easy to add or remove icons?
Here is what my ideal icon system looks like:
To add icons, you just drop them into a designated icons folder. If you no longer need an icon, you simply delete it.
To use the rocket.svg icon in a template, the syntax is as simple as <svg-icon icon="rocket" />.
The icons can be scaled and colored using the CSS font-size and color properties (just like an icon font).
If multiple instances of the same icon appear on the page, the SVG code is not duplicated each time.
No webpack config editing is required.
This is what we will build by writing two small, single-file components. There are a few specific requirements for this implementation, though I’m sure many of you wizards out there could rework this system for other frameworks and build tools:
webpack: If you used the Vue CLI to scaffold your app, then you’re already using webpack.
svg-inline-loader: This allows us to load all of our SVG code and clean up portions we do not want. Go ahead and run npm install svg-inline-loader --save-dev from the terminal to get started.
The SVG sprite component
To meet our requirement of not repeating SVG code for each instance of an icon on the page, we need to build an SVG “sprite.” If you haven’t heard of an SVG sprite before, think of it as a hidden SVG that houses other SVGs. Anywhere we need to display an icon, we can copy it out of the sprite by referencing the id of the icon inside a <use> tag like this:
<svg><use xlink:href="#rocket" /></svg>
That little bit of code is essentially how our <SvgIcon> component will work, but let’s go ahead create the <SvgSprite> component first. Here is the entire SvgSprite.vue file; some of it may seem daunting at first, but I will break it all down.
<!-- SvgSprite.vue --> <template>   <svg width="0" height="0" style="display: none;" v-html="$options.svgSprite" /> </template> <script> const svgContext = require.context(   '!svg-inline-loader?' +    'removeTags=true' + // remove title tags, etc.   '&removeSVGTagAttrs=true' + // enable removing attributes   '&removingTagAttrs=fill' + // remove fill attributes   '!@/assets/icons', // search this directory   true, // search subdirectories   /\w+\.svg$/i // only include SVG files ) const symbols = svgContext.keys().map(path => {   // get SVG file content   const content = svgContext(path)    // extract icon id from filename   const id = path.replace(/^\.\/(.*)\.\w+$/, '$1')   // replace svg tags with symbol tags and id attribute   return content.replace('<svg', `<symbol id="${id}"`).replace('svg>', 'symbol>') }) export default {   name: 'SvgSprite',   svgSprite: symbols.join('\n'), // concatenate all symbols into $options.svgSprite } </script>
In the template, our lone <svg> element has its content bound to $options.svgSprite. In case you’re unfamiliar with $options it contains properties that are directly attached to our Vue component. We could have attached svgSprite to our component’s data, but we don’t really need Vue to set up reactivity for this since our SVG loader is only going to run when our app builds.
In our script, we use require.context to retrieve all of our SVG files and clean them up while we’re at it. We invoke svg-inline-loader and pass it several parameters using syntax that is very similar to query string parameters. I’ve broken these up into multiple lines to make them easier to understand.
const svgContext = require.context( '!svg-inline-loader?' + 'removeTags=true' + // remove title tags, etc. '&removeSVGTagAttrs=true' + // enable removing attributes '&removingTagAttrs=fill' + // remove fill attributes '!@/assets/icons', // search this directory true, // search subdirectories /\w+\.svg$/i // only include SVG files )
What we’re basically doing here is cleaning up the SVG files that live in a specific directory (/assets/icons) so that they’re in good shape to use anywhere we need them.
The removeTags parameter strips out tags that we do not need for our icons, such as title and style. We especially want to remove title tags since those can cause unwanted tooltips. If you would like to preserve any hard-coded styling in your icons, then add removingTags=title as an additional parameter so that only title tags are removed.
We also tell our loader to remove fill attributes, so that we can set our own fill colors with CSS later. It’s possible you will want to retain your fill colors. If that’s the case, then simply remove the removeSVGTagAttrs and removingTagAttrs parameters.
The last loader parameter is the path to our SVG icon folder. We then provide require.context with two more parameters so that it searches subdirectories and only loads SVG files.
In order to nest all of our SVG elements inside our SVG sprite, we have to convert them from <svg> elements into SVG <symbol> elements. This is as simple as changing the tag and giving each one a unique id, which we extract from the filename.
const symbols = svgContext.keys().map(path => { // extract icon id from filename const id = path.replace(/^\.\/(.*)\.\w+$/, '$1') // get SVG file content const content = svgContext(path) // replace svg tags with symbol tags and id attribute return content.replace('<svg', `<symbol id="${id}"`).replace('svg>', 'symbol>') })
What do we do with this <SvgSprite> component? We place it on our page before any icons that depend on it. I recommend adding it to the top of the App.vue file.
<!-- App.vue --> <template>   <div id="app">     <svg-sprite /> <!-- ... -->
The icon component
Now let’s build the SvgIcon.vue component.
<!-- SvgIcon.vue --> <template>   <svg class="icon" :class="{ 'icon-spin': spin }">     <use :xlink:href="`#${icon}`" />   </svg> </template> <script> export default {   name: 'SvgIcon',   props: {     icon: {       type: String,       required: true,     },     spin: {       type: Boolean,       default: false,     },   }, } </script> <style> svg.icon {   fill: currentColor;   height: 1em;   margin-bottom: 0.125em;   vertical-align: middle;   width: 1em; } svg.icon-spin {   animation: icon-spin 2s infinite linear; } @keyframes icon-spin {   from {     transform: rotate(0deg);   }   to {     transform: rotate(359deg);   } } </style>
This component is much simpler. As previously mentioned, we leverage the <use> tag to reference an id inside our sprite. That id comes from our component’s icon prop.
I’ve added a spin prop in there that toggles an .icon-spin class as an optional bit of animation, should we ever need. This could, for example, be useful for a loading spinner icon.
<svg-icon v-if="isLoading" icon="spinner" spin />
Depending on your needs, you may want to add additional props, such as rotate or flip. You could simply add the classes directly to the component without using props if you’d like.
Most of our component’s content is CSS. Other than the spinning animation, most of this is used to make our SVG icon act more like an icon font¹. To align the icons to the text baseline, I’ve found that applying vertical-align: middle, along with a bottom margin of 0.125em, works for most cases. We also set the fill attribute value to currentColor, which allows us to color the icon just like text.
<p style="font-size: 2em; color: red;">   <svg-icon icon="exclamation-circle" /><!-- This icon will be 2em and red. -->   Error! </p>
That’s it!  If you want to use the icon component anywhere in your app without having to import it into every component that needs it, be sure to register the component in your main.js file:
// main.js import Vue from 'vue' import SvgIcon from '@/components/SvgIcon.vue' Vue.component('svg-icon', SvgIcon) // ...
Final thoughts
Here are a few ideas for improvements, which I intentionally left out to keep this solution approachable:
Scale icons that have non-square dimensions to maintain their proportions
Inject the SVG sprite into the page without needing an additional component.
Make it work with vite, which is a new, fast (and webpack-free) build tool from Vue creator Evan You.
Leverage the Vue 3 Composition API.
If you want to quickly take these components for a spin, I’ve created a demo app based on the default vue-cli template. I hope this helps you develop an implementation that fits your app’s needs!
¹ If you’re wondering why we’re using SVG when we want it to behave like an icon font, then check out the classic post that pits the two against one another.
The post A Font-Like SVG Icon System for Vue appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.
A Font-Like SVG Icon System for Vue published first on https://deskbysnafu.tumblr.com/
0 notes
mbaljeetsingh · 4 years
Text
An Overview of What's Coming in Vue 3
At the time of this writing, Vue 3.0 is at its 10th alpha version. Expect a faster, smaller, more maintainable, and easier to use version of the Vue you know and love. You can still use Vue via a script tag and your Vue 2.x code will continue to work. But you can start playing with the alpha version of Vue 3.0 here and we’re going to get into some of what v3 is offering.
Among other things, there’s a new API for creating components. It doesn’t introduce new concepts to Vue, but rather exposes Vue’s core capabilities like creating and observing reactive state as standalone functions. This is ultimately useful to Vue developers of all levels.
Options API and Composition API
In Vue 2, components are created with the object-based Options API. Vue 3 adds a set of APIs, referred to as the Composition API, which is function-based. This is primarily to address two issues that Vue 2 ran into for very large projects.
In large components that encapsulate multiple logical tasks, you want to group code by feature, but the nature of the Options API is that such code gets split up (among lifecycle hooks and so on), negatively affecting readability. Secondly, you want to be able to reuse logic in large-scale projects, and in Vue 2, solutions like mixins don’t address either issue very well.
Vue 3 seeks to kill both birds with one stone by exposing a new API. This API will live alongside the Options API, not replace it. This means that you can go on building components in the way that you’re used to without encountering any problems. But, you can also start building with the Composition API, which provides more flexible code organization and logic reuse capabilities as well as other improvements.
Even if the problems it specifically addresses are not pertinent to you, the new API has clearly had a lot of thought go into it to push Vue forward as a framework, for instance, by reducing the extent to which Vue operates “magically” behind the scenes.
— Sorry to interrupt this program! 📺
If you're interested in learning Vue in a comprehensive and structured way, I highly recommend you try The Vue.js Master Class course by Vue School. Learning from a premium resource like that is a serious investment in yourself.
Plus, this is an affiliate link, so if you purchase the course you help Alligator.io continue to exist at the same time! 🙏
- Seb, ✌️+❤️
Composition API
The Composition API is available now as a plugin for Vue 2 so you can try it out. It will be shipped baked-in in Vue 3.
In Vue 2 reactivity was achieved through the getters and setters of Object.defineProperty. This caused some limitations which you’ve already probably experienced (e.g.: updating an Array by index). In Vue 3, reactivity is accomplished through proxies, a feature that was introduced in JavaScript ES6.
You need not have a Vue instance to use the new reactivity API. It offers standalone APIs which allow you to create, observe, and react to state changes.
You would first import { reactive } from 'vue'. Then, you could create an object in the following way:
const state = reactive({ count: 0 })
You’ll have access to APIs that will allow you to dynamically inject component lifecycle hooks into a Vue instance.
The lifecycle registration methods can only be used in the setup() method which is the entry point where all the composition functions are called. For instance:
import { onMounted } from 'vue' export default { setup() { onMounted(() => { console.log('component is mounted.') }) } }
Functions that use these APIs can be imported into a component, allowing the component to do multiple logical tasks with reusable and readable code.
TypeScript
The composition API also offers better TypeScript support. It’s supposed to result in better type inferences with bindings returned from setup() and props declarations used to infer types.
Component code using TypeScript and JavaScript will look largely identical and TypeScript definitions benefit JavaScript users as well, say, if they use an IDE like Visual Studio Code.
View Declaration
Vue 2 supports templates as well as render functions. You don’t need to know an awful lot here except that Vue 3 continues to support both while optimizing rendering speed (such as by speeding up diff algorithms that operate under the hood so that Vue knows what needs to be re-rendered).
Faster
Virtual DOM has been rewritten from the ground-up to make for faster mounting and patching.
Compile-time hints have been added to reduce runtime overhead. This means skipping unnecessary condition branches and avoiding re-renders. Static tree and static prop hoisting means entire trees and nodes can skip being patched. Inline functions (like in a handler for a component in a template) won’t cause unnecessary re-renders.
You’re going to get a proxy-based observation mechanism with full language coverage and better performance. Instance properties will be proxied faster using native Proxy instead of Object.defineProperty like before.
You can expect up to 100% faster component instance initialization with double the speed and half the memory usage. 🏎️🏎️🏎️
Smaller
Vue 3 is also smaller.
It is tree shaking-friendly. Tree shaking refers to shaking off unused code. The core runtime has gone from ~20kb in size, gzipped, to ~10kb, gzipped.
The size of the Vue bundle increases with each new feature but, by providing most global APIs and Vue helpers as ES module exports, Vue 3 makes more code tree shakeable, even template code.
Coherence
Libraries like Vue Router and test-utils will be updated to line up with the new Vue. Vue now has a custom renderer API (similar to React Native for those who want to use it to create renderers for mobile or other host environments.
Conclusion
There is a ton to look forward to with Vue 3 with more like Portals that couldn’t fit in this short post. The new Composition API moves us towards an all around better Vue. An exact release date is not set but it’s coming soon. Get a head start now!
via Alligator.io https://ift.tt/2RdfYbt
0 notes
siliconwebx · 5 years
Text
What Hooks Mean for Vue
Not to be confused with Lifecycle Hooks, Hooks were introduced in React in v16.7.0-alpha, and a proof of concept was released for Vue a few days after. Even though it was proposed by React, it’s actually an important composition mechanism that has benefits across JavaScript framework ecosystems, so we’ll spend a little time today discussing what this means.
Mainly, Hooks offer a more explicit way to think of reusable patterns — one that avoids rewrites to the components themselves and allows disparate pieces of the stateful logic to seamlessly work together.
The initial problem
In terms of React, the problem was this: classes were the most common form of components when expressing the concept of state. Stateless functional components were also quite popular, but due to the fact that they could only really render, their use was limited to presentational tasks.
Classes in and of themselves present some issues. For example, as React became more ubiquitous, stumbling blocks for newcomers did as well. In order to understand React, one had to understand classes, too. Binding made code verbose and thus less legible, and an understanding of this in JavaScript was required. There are also some optimization stumbling blocks that classes present, discussed here.
In terms of the reuse of logic, it was common to use patterns like render props and higher-order components, but we’d find ourselves in similar “pyramid of doom” — style implementation hell where nesting became so heavily over-utilized that components could be difficult to maintain. This led me to ranting drunkenly at Dan Abramov, and nobody wants that.
Hooks address these concerns by allowing us to define a component's stateful logic using only function calls. These function calls become more compose-able, reusable, and allows us to express composition in functions while still accessing and maintaining state. When hooks were announced in React, people were excited — you can see some of the benefits illustrated here, with regards to how they reduce code and repetition:
Took @dan_abramov's code from #ReactConf2018 and visualised it so you could see the benefits that React Hooks bring us. pic.twitter.com/dKyOQsG0Gd
— Pavel Prichodko (@prchdk) October 29, 2018
In terms of maintenance, simplicity is key, and Hooks provide a single, functional way of approaching shared logic with the potential for a smaller amount of code.
Why Hooks in Vue?
You may read through this and wonder what Hooks have to offer in Vue. It seems like a problem that doesn’t need solving. After all, Vue doesn’t predominantly use classes. Vue offers stateless functional components (should you need them), but why would we need to carry state in a functional component? We have mixins for composition where we can reuse the same logic for multiple components. Problem solved.
I thought the same thing, but after talking to Evan You, he pointed out a major use case I missed: mixins can’t consume and use state from one to another, but Hooks can. This means that if we need chain encapsulated logic, it’s now possible with Hooks.
Hooks achieve what mixins do, but avoid two main problems that come with mixins:
They allows us to pass state from one to the other.
They make it explicit where logic is coming from.
If we’re using more than one mixin, it’s not clear which property was provided by which mixin. With Hooks, the return value of the function documents the value being consumed.
So, how does that work in Vue? We mentioned before that, when working with Hooks, logic is expressed in function calls that become reusable. In Vue, this means that we can group a data call, a method call, or a computed call into another custom function, and make them freely compose-able. Data, methods, and computed now become available in functional components.
Example
Let’s go over a really simple hook so that we can understand the building blocks before we move on to an example of composition in Hooks.
useWat?
OK, here’s were we have, what you might call, a crossover event between React and Vue. The use prefix is a React convention, so if you look up Hooks in React, you’ll find things like useState, useEffect, etc. More info here.
In Evan’s live demo, you can see where he’s accessing useState and useEffect for a render function.
If you’re not familiar with render functions in Vue, it might be helpful to take a peek at that.
But when we’re working with Vue-style Hooks, we’ll have — you guessed it — things like: useData, useComputed, etc.
So, in order for us look at how we'd use Hooks in Vue, I created a sample app for us to explore.
Demo Site
GitHub Repo
In the src/hooks folder, I've created a hook that prevents scrolling on a useMounted hook and reenables it on useDestroyed. This helps me pause the page when we're opening a dialog to view content, and allows scrolling again when we're done viewing the dialog. This is good functionality to abstract because it would probably be useful several times throughout an application.
import { useDestroyed, useMounted } from "vue-hooks"; export function preventscroll() { const preventDefault = (e) => { e = e || window.event; if (e.preventDefault) e.preventDefault(); e.returnValue = false; } // keycodes for left, up, right, down const keys = { 37: 1, 38: 1, 39: 1, 40: 1 }; const preventDefaultForScrollKeys = (e) => { if (keys[e.keyCode]) { preventDefault(e); return false; } } useMounted(() => { if (window.addEventListener) // older FF window.addEventListener('DOMMouseScroll', preventDefault, false); window.onwheel = preventDefault; // modern standard window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE window.touchmove = preventDefault; // mobile window.touchstart = preventDefault; // mobile document.onkeydown = preventDefaultForScrollKeys; }); useDestroyed(() => { if (window.removeEventListener) window.removeEventListener('DOMMouseScroll', preventDefault, false); //firefox window.addEventListener('DOMMouseScroll', (e) => { e.stopPropagation(); }, true); window.onmousewheel = document.onmousewheel = null; window.onwheel = null; window.touchmove = null; window.touchstart = null; document.onkeydown = null; }); }
And then we can call it in a Vue component like this, in AppDetails.vue:
<script> import { preventscroll } from "./../hooks/preventscroll.js"; ... export default { ... hooks() { preventscroll(); } } </script>
We're using it in that component, but now we can use the same functionality throughout the application!
Two Hooks, understanding each other
We mentioned before that one of the primary differences between hooks and mixins is that hooks can actually pass values from one to another. Let's look at that with a simple, albeit slightly contrived, example.
Let's say in our application we need to do calculations in one hook that will be reused elsewhere, and something else that needs to use that calculation. In our example, we have a hook that takes the window width and passes it into an animation to let it know to only fire when we're on larger screens.
In the first hook:
import { useData, useMounted } from 'vue-hooks'; export function windowwidth() { const data = useData({ width: 0 }) useMounted(() => { data.width = window.innerWidth }) // this is something we can consume with the other hook return { data } }
Then, in the second we use this to create a conditional that fires the animation logic:
// the data comes from the other hook export function logolettering(data) { useMounted(function () { // this is the width that we stored in data from the previous hook if (data.data.width > 1200) { // we can use refs if they are called in the useMounted hook const logoname = this.$refs.logoname; Splitting({ target: logoname, by: "chars" }); TweenMax.staggerFromTo(".char", 5, { opacity: 0, transformOrigin: "50% 50% -30px", cycle: { color: ["red", "purple", "teal"], rotationY(i) { return i * 50 } } }, ...
Then, in the component itself, we'll pass one into the other:
<script> import { logolettering } from "./../hooks/logolettering.js"; import { windowwidth } from "./../hooks/windowwidth.js"; export default { hooks() { logolettering(windowwidth()); } }; </script>
Now we can compose logic with Hooks throughout our application! Again, this is a contrived example for the purposes of demonstration, but you can see how useful this might be for large scale applications to keep things in smaller, reusable functions.
Future plans
Vue Hooks are already available to use today with Vue 2.x, but are still experimental. We’re planning on integrating Hooks into Vue 3, but will likely deviate from React’s API in our own implementation. We find React Hooks to be very inspiring and are thinking about how to introduce its benefits to Vue developers. We want to do it in a way that complements Vue's idiomatic usage, so there's still a lot of experimentation to do.
You can get started by checking out the repo here. Hooks will likely become a replacement for mixins, so although the feature still in its early stages, it’s probably a concept that would be beneficial to explore in the meantime.
(Sincere thanks to Evan You and Dan Abramov for proofing this article.)
The post What Hooks Mean for Vue appeared first on CSS-Tricks.
😉SiliconWebX | 🌐CSS-Tricks
0 notes
suzanneshannon · 5 years
Text
An Early Look at the Vue 3 Composition API in the Wild
I recently had an opportunity to try the new Vue Composition API in a real project to check where it might be useful and how we could use it in the future.
Until now, when we were creating a new component we were using Options API. That API forced us to separate the component’s code by options, meaning that we needed to have all reactive data in one place (data), all computed properties in one place (computed), all methods in one place (methods), and so on.
As it is handy and readable for smaller components, it becomes painful when the component gets more complicated and deals with multiple functionalities. Usually, logic related to one specific functionality contains some reactive data, computed property, a method or a few of them; sometimes it also involves using component lifecycle hooks. That makes you constantly jump between different options in the code when working on a single logical concern.
The other issue that you may have encountered when working with Vue is how to extract a common logic that can be reused by multiple components. Vue already has few options to do that, but all of them have their own drawbacks (e.g. mixins, and scoped-slots).
The Composition API brings a new way of creating component, separating code and extracting reusable pieces of code.
Let’s start with code composition within a component.
Code composition
Imagine you have a main component that sets up few things for your whole Vue app (like layout in Nuxt). It deals with the following things:
setting locale
checking if the user is still authenticated and redirects them if not
preventing the user from reloading the app too many times
tracking user activity and reacting when the user is inactive for specific period of time
listening on an event using EventBus (or window object event)
Those are just a few things the component can do. You can probably imagine a more complex component, but this will serve the purpose of this example. For the sake of readability, I am just using names of the props without the actual implementation.
This is how the component would look like using Options API:
<template> <div id="app"> ... </div> </template> <script> export default { name: 'App', data() { return { userActivityTimeout: null, lastUserActivityAt: null, reloadCount: 0 } }, computed: { isAuthenticated() {...} locale() {...} }, watch: { locale(value) {...}, isAuthenticated(value) {...} }, async created() { const initialLocale = localStorage.getItem('locale') await this.loadLocaleAsync(initialLocale) }, mounted() { EventBus.$on(MY_EVENT, this.handleMyEvent) this.setReloadCount() this.blockReload() this.activateActivityTracker() this.resetActivityTimeout() }, beforeDestroy() { this.deactivateActivityTracker() clearTimeout(this.userActivityTimeout) EventBus.$off(MY_EVENT, this.handleMyEvent) }, methods: { activateActivityTracker() {...}, blockReload() {...}, deactivateActivityTracker() {...}, handleMyEvent() {...}, async loadLocaleAsync(selectedLocale) {...} redirectUser() {...} resetActivityTimeout() {...}, setI18nLocale(locale) {...}, setReloadCount() {...}, userActivityThrottler() {...}, } } </script>
As you can see, each option contains parts from all functionalities. There is no clear separation between them and that makes the code hard to read, especially if you are not the person who wrote it and you are looking at it for the first time. It is very hard to find which method is used by which functionality.
Let’s look at it again but identify the logical concerns as comments. Those would be:
Activity tracker
Reload blocker
Authentication check
Locale
Event Bus registration
<template> <div id="app"> ... </div> </template> <script> export default { name: 'App', data() { return { userActivityTimeout: null, // Activity tracker lastUserActivityAt: null, // Activity tracker reloadCount: 0 // Reload blocker } }, computed: { isAuthenticated() {...} // Authentication check locale() {...} // Locale }, watch: { locale(value) {...}, isAuthenticated(value) {...} // Authentication check }, async created() { const initialLocale = localStorage.getItem('locale') // Locale await this.loadLocaleAsync(initialLocale) // Locale }, mounted() { EventBus.$on(MY_EVENT, this.handleMyEvent) // Event Bus registration this.setReloadCount() // Reload blocker this.blockReload() // Reload blocker this.activateActivityTracker() // Activity tracker this.resetActivityTimeout() // Activity tracker }, beforeDestroy() { this.deactivateActivityTracker() // Activity tracker clearTimeout(this.userActivityTimeout) // Activity tracker EventBus.$off(MY_EVENT, this.handleMyEvent) // Event Bus registration }, methods: { activateActivityTracker() {...}, // Activity tracker blockReload() {...}, // Reload blocker deactivateActivityTracker() {...}, // Activity tracker handleMyEvent() {...}, // Event Bus registration async loadLocaleAsync(selectedLocale) {...} // Locale redirectUser() {...} // Authentication check resetActivityTimeout() {...}, // Activity tracker setI18nLocale(locale) {...}, // Locale setReloadCount() {...}, // Reload blocker userActivityThrottler() {...}, // Activity tracker } } </script>
See how hard it is to untangle all of those? 🙂
Now imagine you need to make a change in one functionality (e.g. activity tracking logic). Not only do you need to know which elements are related to that logic, but even when you know, you still need to jump up and down between different component options.
Let’s use the Composition API to separate the code by logical concerns. To do that we create a single function for each logic related to a specific functionality. This is what we call a composition function.
// Activity tracking logic function useActivityTracker() { const userActivityTimeout = ref(null) const lastUserActivityAt = ref(null) function activateActivityTracker() {...} function deactivateActivityTracker() {...} function resetActivityTimeout() {...} function userActivityThrottler() {...} onBeforeMount(() => { activateActivityTracker() resetActivityTimeout() }) onUnmounted(() => { deactivateActivityTracker() clearTimeout(userActivityTimeout.value) }) }
// Reload blocking logic function useReloadBlocker(context) { const reloadCount = ref(null) function blockReload() {...} function setReloadCount() {...} onMounted(() => { setReloadCount() blockReload() }) }
// Locale logic function useLocale(context) { async function loadLocaleAsync(selectedLocale) {...} function setI18nLocale(locale) {...} watch(() => { const locale = ... loadLocaleAsync(locale) }) // No need for a 'created' hook, all logic that runs in setup function is placed between beforeCreate and created hooks const initialLocale = localStorage.getItem('locale') loadLocaleAsync(initialLocale) }
// Event bus listener registration import EventBus from '@/event-bus' function useEventBusListener(eventName, handler) { onMounted(() => EventBus.$on(eventName, handler)) onUnmounted(() => EventBus.$off(eventName, handler)) }
As you can see, we can declare reactive data (ref / reactive), computed props, methods (plain functions), watchers (watch) and lifecycle hooks (onMounted / onUnmounted). Basically everything you normally use in a component.
We have two options when it comes to where to keep the code. We can leave it inside the component or extract it into a separate file. Since the Composition API is not officially there yet, there are no best practices or rules on how to deal with it. The way I see it, if the logic is tightly coupled to a specific component (i.e. it won’t be reused anywhere else), and it can’t live without the component itself, I suggest leaving it within the component. On the flip side, if it is general functionality that will likely be reused, I suggest extracting it to a separate file. However, if we want to keep it in a separate file, we need to remember to export the function from the file and import it in our component.
This is how our component will look like using newly created composition functions:
<template> <div id="app"> </div> </template> <script> export default { name: 'App', setup(props, context) { useEventBusListener(MY_EVENT, handleMyEvent) useActivityTracker() useReloadBlocker(context) useLocale(context) const isAuthenticated = computed(() => ...) watch(() => { if (!isAuthenticated) {...} }) function handleMyEvent() {...}, function useLocale() {...} function useActivityTracker() {...} function useEventBusListener() {...} function useReloadBlocker() {...} } } </script>
This gives us a single function for each logical concern. If we want to use any specific concern, we need to call the related composition function in the new setup function.
Imagine again that you need to make some change in activity tracking logic. Everything related to that functionality lives in the useActivityTracker function. Now you instantly know where to look and jump to the right place to see all the related pieces of code. Beautiful!
Extracting reusable pieces of code
In our case, the Event Bus listener registration looks like a piece of code we can use in any component that listens to events on Event Bus.
As mentioned before, we can keep the logic related to a specific functionality in a separate file. Let’s move our Event Bus listener setup into a separate file.
// composables/useEventBusListener.js import EventBus from '@/event-bus' export function useEventBusListener(eventName, handler) { onMounted(() => EventBus.$on(eventName, handler)) onUnmounted(() => EventBus.$off(eventName, handler)) }
To use it in a component, we need to make sure we export our function (named or default) and import it in a component.
<template> <div id="app"> ... </div> </template> <script> import { useEventBusListener } from '@/composables/useEventBusListener' export default { name: 'MyComponent', setup(props, context) { useEventBusListener(MY_EVENT, myEventHandled) useEventBusListener(ANOTHER_EVENT, myAnotherHandled) } } </script>
That’s it! We can now use that in any component we need.
Wrapping up
There is an ongoing discussion about the Composition API. This post has no intention to promote any side of the discussion. It is more about showing when it might be useful and in what cases it brings additional value.
I think it is always easier to understand the concept on a real life example like above. There are more use cases and, the more you use the new API, the more patterns you will see. This post is merely a few basic patterns to get your started.
Let’s go again through the presented use cases and see where the Composition API can be useful:
General features that can live on its own without tight coupling with any specific component
All logic related to a specific feature in one file
Keep it in @/composables/*.js and import it in components
Examples: Activity Tracker, Reload Blocker, and Locale
Reusable features that are used in multiple components
All logic related to a specific feature in one file
Keep it in @/composables/*.js and import in components
Examples: Event Bus listener registration, window event registration, common animation logic, common library usage
Code organization within component
All logic related to a specific feature in one function
Keep the code in a composition function within the component
The code related to the same logical concern is in the same place (i.e. there’s no need to jump between data, computed, methods, lifecycle hooks, etc.)
Remember: This is all a work-in-progress!
The Vue Composition API is currently at work in progress stage and is subject to future changes. Nothing mentioned in the examples above is sure, and both syntax and use cases may change. It is intended to be shipped with Vue version 3.0. In the meantime, you can check out view-use-web for a collection of composition functions that are expected to be included in Vue 3 but can be used with the Composition API in Vue 2.
If you want to experiment with the new API you can use the @vue/composition library.
The post An Early Look at the Vue 3 Composition API in the Wild appeared first on CSS-Tricks.
An Early Look at the Vue 3 Composition API in the Wild published first on https://deskbysnafu.tumblr.com/
0 notes
suzanneshannon · 5 years
Text
What Hooks Mean for Vue
Not to be confused with Lifecycle Hooks, Hooks were introduced in React in v16.7.0-alpha, and a proof of concept was released for Vue a few days after. Even though it was proposed by React, it’s actually an important composition mechanism that has benefits across JavaScript framework ecosystems, so we’ll spend a little time today discussing what this means.
Mainly, Hooks offer a more explicit way to think of reusable patterns — one that avoids rewrites to the components themselves and allows disparate pieces of the stateful logic to seamlessly work together.
The initial problem
In terms of React, the problem was this: classes were the most common form of components when expressing the concept of state. Stateless functional components were also quite popular, but due to the fact that they could only really render, their use was limited to presentational tasks.
Classes in and of themselves present some issues. For example, as React became more ubiquitous, stumbling blocks for newcomers did as well. In order to understand React, one had to understand classes, too. Binding made code verbose and thus less legible, and an understanding of this in JavaScript was required. There are also some optimization stumbling blocks that classes present, discussed here.
In terms of the reuse of logic, it was common to use patterns like render props and higher-order components, but we’d find ourselves in similar “pyramid of doom” — style implementation hell where nesting became so heavily over-utilized that components could be difficult to maintain. This led me to ranting drunkenly at Dan Abramov, and nobody wants that.
Hooks address these concerns by allowing us to define a component's stateful logic using only function calls. These function calls become more compose-able, reusable, and allows us to express composition in functions while still accessing and maintaining state. When hooks were announced in React, people were excited — you can see some of the benefits illustrated here, with regards to how they reduce code and repetition:
Took @dan_abramov's code from #ReactConf2018 and visualised it so you could see the benefits that React Hooks bring us. pic.twitter.com/dKyOQsG0Gd
— Pavel Prichodko (@prchdk) October 29, 2018
In terms of maintenance, simplicity is key, and Hooks provide a single, functional way of approaching shared logic with the potential for a smaller amount of code.
Why Hooks in Vue?
You may read through this and wonder what Hooks have to offer in Vue. It seems like a problem that doesn’t need solving. After all, Vue doesn’t predominantly use classes. Vue offers stateless functional components (should you need them), but why would we need to carry state in a functional component? We have mixins for composition where we can reuse the same logic for multiple components. Problem solved.
I thought the same thing, but after talking to Evan You, he pointed out a major use case I missed: mixins can’t consume and use state from one to another, but Hooks can. This means that if we need chain encapsulated logic, it’s now possible with Hooks.
Hooks achieve what mixins do, but avoid two main problems that come with mixins:
They allows us to pass state from one to the other.
They make it explicit where logic is coming from.
If we’re using more than one mixin, it’s not clear which property was provided by which mixin. With Hooks, the return value of the function documents the value being consumed.
So, how does that work in Vue? We mentioned before that, when working with Hooks, logic is expressed in function calls that become reusable. In Vue, this means that we can group a data call, a method call, or a computed call into another custom function, and make them freely compose-able. Data, methods, and computed now become available in functional components.
Example
Let’s go over a really simple hook so that we can understand the building blocks before we move on to an example of composition in Hooks.
useWat?
OK, here’s were we have, what you might call, a crossover event between React and Vue. The use prefix is a React convention, so if you look up Hooks in React, you’ll find things like useState, useEffect, etc. More info here.
In Evan’s live demo, you can see where he’s accessing useState and useEffect for a render function.
If you’re not familiar with render functions in Vue, it might be helpful to take a peek at that.
But when we’re working with Vue-style Hooks, we’ll have — you guessed it — things like: useData, useComputed, etc.
So, in order for us look at how we'd use Hooks in Vue, I created a sample app for us to explore.
Demo Site
GitHub Repo
In the src/hooks folder, I've created a hook that prevents scrolling on a useMounted hook and reenables it on useDestroyed. This helps me pause the page when we're opening a dialog to view content, and allows scrolling again when we're done viewing the dialog. This is good functionality to abstract because it would probably be useful several times throughout an application.
import { useDestroyed, useMounted } from "vue-hooks"; export function preventscroll() { const preventDefault = (e) => { e = e || window.event; if (e.preventDefault) e.preventDefault(); e.returnValue = false; } // keycodes for left, up, right, down const keys = { 37: 1, 38: 1, 39: 1, 40: 1 }; const preventDefaultForScrollKeys = (e) => { if (keys[e.keyCode]) { preventDefault(e); return false; } } useMounted(() => { if (window.addEventListener) // older FF window.addEventListener('DOMMouseScroll', preventDefault, false); window.onwheel = preventDefault; // modern standard window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE window.touchmove = preventDefault; // mobile window.touchstart = preventDefault; // mobile document.onkeydown = preventDefaultForScrollKeys; }); useDestroyed(() => { if (window.removeEventListener) window.removeEventListener('DOMMouseScroll', preventDefault, false); //firefox window.addEventListener('DOMMouseScroll', (e) => { e.stopPropagation(); }, true); window.onmousewheel = document.onmousewheel = null; window.onwheel = null; window.touchmove = null; window.touchstart = null; document.onkeydown = null; }); }
And then we can call it in a Vue component like this, in AppDetails.vue:
<script> import { preventscroll } from "./../hooks/preventscroll.js"; ... export default { ... hooks() { preventscroll(); } } </script>
We're using it in that component, but now we can use the same functionality throughout the application!
Two Hooks, understanding each other
We mentioned before that one of the primary differences between hooks and mixins is that hooks can actually pass values from one to another. Let's look at that with a simple, albeit slightly contrived, example.
Let's say in our application we need to do calculations in one hook that will be reused elsewhere, and something else that needs to use that calculation. In our example, we have a hook that takes the window width and passes it into an animation to let it know to only fire when we're on larger screens.
In the first hook:
import { useData, useMounted } from 'vue-hooks'; export function windowwidth() { const data = useData({ width: 0 }) useMounted(() => { data.width = window.innerWidth }) // this is something we can consume with the other hook return { data } }
Then, in the second we use this to create a conditional that fires the animation logic:
// the data comes from the other hook export function logolettering(data) { useMounted(function () { // this is the width that we stored in data from the previous hook if (data.data.width > 1200) { // we can use refs if they are called in the useMounted hook const logoname = this.$refs.logoname; Splitting({ target: logoname, by: "chars" }); TweenMax.staggerFromTo(".char", 5, { opacity: 0, transformOrigin: "50% 50% -30px", cycle: { color: ["red", "purple", "teal"], rotationY(i) { return i * 50 } } }, ...
Then, in the component itself, we'll pass one into the other:
<script> import { logolettering } from "./../hooks/logolettering.js"; import { windowwidth } from "./../hooks/windowwidth.js"; export default { hooks() { logolettering(windowwidth()); } }; </script>
Now we can compose logic with Hooks throughout our application! Again, this is a contrived example for the purposes of demonstration, but you can see how useful this might be for large scale applications to keep things in smaller, reusable functions.
Future plans
Vue Hooks are already available to use today with Vue 2.x, but are still experimental. We’re planning on integrating Hooks into Vue 3, but will likely deviate from React’s API in our own implementation. We find React Hooks to be very inspiring and are thinking about how to introduce its benefits to Vue developers. We want to do it in a way that complements Vue's idiomatic usage, so there's still a lot of experimentation to do.
You can get started by checking out the repo here. Hooks will likely become a replacement for mixins, so although the feature still in its early stages, it’s probably a concept that would be beneficial to explore in the meantime.
(Sincere thanks to Evan You and Dan Abramov for proofing this article.)
The post What Hooks Mean for Vue appeared first on CSS-Tricks.
What Hooks Mean for Vue published first on https://deskbysnafu.tumblr.com/
0 notes