At Vue’s End

A presentation at Boston VueJS in July 2019 in Boston, MA, USA by Divya

Slide 1

Slide 1

Slide 2

Slide 2

Slide 3

Slide 3

Slide 4

Slide 4

Slide 5

Slide 5

Slide 6

Slide 6

Don’t Hate the Player Hate the GAME

Slide 7

Slide 7

What’s in Vue3?

Slide 8

Slide 8

Nutrition Facts Serving Size Amount Per Serving Calories 0 Warnings Do not consume While reading Hacker News Consume in moderation

Slide 9

Slide 9

Slide 10

Slide 10

<template> <div> Hello {{ msg }} </div> </template> <script> export default { name: “HelloWorld”, props: { msg: String }, } </script>

Slide 11

Slide 11

<template> <div> Hello {{ msg }} </div> </template> <script> export default { name: “HelloWorld”, props: { msg: String }, setup(props, context) { } } </script>

Slide 12

Slide 12

<template> <div> {{ msg }} </div> </template> <script> import { value } from “vue-function-api”; export default { name: “HelloWorld”, props: { name: String }, data() { return { msg: hello ${this.name}; } }, }; </script>

Slide 13

Slide 13

<template> <div> {{ msg }} </div> </template> <script> import { value } from “vue-function-api”; export default { name: “HelloWorld”, props: { name: String }, data() { return { msg: hello ${this.name}; } }, setup(props) { … } }; </script>

Slide 14

Slide 14

<template> <div> {{ msg }} </div> </template> like data

<script> export default { name: “HelloWorld”, props: { name: String }, setup(props) { return { msg: `hello ${props.name}!` } } } </script>

Slide 15

Slide 15

<template> <div> {{ msg }} </div> </template> only available in render context

<script> export default { name: “HelloWorld”, props: { name: String }, setup(props) { return { msg: `hello ${props.name}!` } } } </script>

Slide 16

Slide 16

Slide 17

Slide 17

<template> <div> <h2>Hello {{ text }}</h2> <div> <input v-model=”text”> </div> </div> </template>

Slide 18

Slide 18

<template> <div> <h2>Hello {{ text }}</h2> <div> <input v-model=”text”> </div> </div> </template> <script> import { value } from “vue-function-api”; export default { name: “HelloWorld”, setup(props) { const text = value(null) return { text } } } </script>

Slide 19

Slide 19

Slide 20

Slide 20

<template> data <div> <span>count is {{ count }}</span> <br> <span>plus one is {{ plusOne }}</span> <div> <button @click=”increment”>Increment</button> </div> </div> </template> computed method

<script> import { value, computed, watch } from “vue-function-api”; export default { name: “Counter”, setup() { // reactive state const count = value(0); // computed state const plusOne = computed(() => count.value + 1); // method const increment = () => { count.value++; }; // watch watch(

Slide 21

Slide 21

<script> import { value, computed, watch } from “vue-function-api”; export default { name: “Counter”, setup() { // reactive state const count = value(0); // computed state const plusOne = computed(() => count.value + 1); // method const increment = () => { count.value++; }; // watch watch( () => count.value * 2, val => { console.log(`count * 2 is ${val}`); } ); // expose bindings on render context return { count, plusOne, increment }; }

Slide 22

Slide 22

<script> import { value, computed, watch } from “vue-function-api”; export default { name: “Counter”, setup() { // reactive state const count = value(0); // computed state const plusOne = computed(() => count.value + 1); // method const increment = () => { count.value++; }; // watch watch( () => count.value * 2, val => { console.log(`count * 2 is ${val}`); } ); // expose bindings on render context return { count, plusOne, increment }; }

Slide 23

Slide 23

l a it on i s o p om C Functions Vue Hooks

Slide 24

Slide 24

function useMouse() { const x = value(0) const y = value(0) const update = e => { x.value = e.pageX y.value = e.pageY } onMounted(() => { window.addEventListener(‘mousemove’, update) }) onUnmounted(() => { window.removeEventListener(‘mousemove’, update) }) return { x, y } } // in consuming component const Component = { setup() { const { x, y } = useMouse() const { z } = useOtherLogic() return { x, y, z } }, template: <div>{{ x }} {{ y }} {{ z }}</div> }

Slide 25

Slide 25

import { value, computed } from “vue-function-api”; export default function useCounter() { const count = value(0); const increment = () => { count.value = count.value + 1; }; const plusOne = computed(() => count.value + 1); return { count, increment, plusOne }; }

Slide 26

Slide 26

<template> <div> <span>count is {{ count }}</span> <span>{{ plusOne }}</span> <div> <button @click=”increment”>Increment</button> </div> </div> </template> <script> import useCounter from “../functions/useCounter”; export default { name: “Counter”, setup(props, context) { const { count, increment, plusOne } = useCounter(); return { count, increment, plusOne }; } }; </script>

Slide 27

Slide 27

Theoretical API Actual API

Slide 28

Slide 28

Slide 29

Slide 29

npm install vue-function-api —save yarn add vue-function-api

Slide 30

Slide 30

Slide 31

Slide 31

<template> <div id=”map” ref=”map”></div> </template>

<script> const mapboxgl = require(‘mapbox-gl’); var turf = require(‘turf’) export default { name: “RunnerMap”, data() { return { route: null, map: null, raf: null, timestamp: null, loaded: } }, created() { mapboxgl.accessToken = “API_KEY” fetch(“https://gist.githubusercontent.com/shortdiv/XXXX.geojso .then(response => { return response.json(); }) .then(data => { this.route = data }) }, watch:{ loaded() {

Slide 32

Slide 32

<template> <div id=”map” ref=”map”></div> </template>

<script> const mapboxgl = require(‘mapbox-gl’); var turf = require(‘turf’) export default { name: “RunnerMap”, data() { return { route: null, map: null, raf: null, timestamp: null, loaded: } }, created() { mapboxgl.accessToken = “API_KEY” fetch(“https://gist.githubusercontent.com/shortdiv/XXXX.geojso .then(response => { return response.json(); }) .then(data => { this.route = data }) }, watch:{ loaded() {

Slide 33

Slide 33

mounted() { var map = new mapboxgl.Map({ container: this.$refs.map, // container id style: ‘mapbox://styles/mapbox/dark-v10’, //hosted style id center: [-71.46933442476096,42.25342191415163], // starting zoom: 12 // starting zoom }); this.map = map map.on(‘load’, () => { this.loaded = true map.addSource(‘point’, { “type”: “geojson”, “data”: { “type”: “Feature”, “properties”:{}, “geometry”: { “type”: “Point”, “coordinates”:[-71.51794373989105,42.23001485466201] } } }); map.addLayer({ “id”: “point”, “source”: “point”, “type”: “circle”, “paint”: { “circle-radius”: 10,

Slide 34

Slide 34

mounted() { var map = new mapboxgl.Map({ container: this.$refs.map, // container id style: ‘mapbox://styles/mapbox/dark-v10’, //hosted style id center: [-71.46933442476096,42.25342191415163], // starting zoom: 12 // starting zoom }); this.map = map map.on(‘load’, () => { this.loaded = true map.addSource(‘point’, { “type”: “geojson”, “data”: { “type”: “Feature”, “properties”:{}, “geometry”: { “type”: “Point”, “coordinates”:[-71.51794373989105,42.23001485466201] } } }); map.addLayer({ “id”: “point”, “source”: “point”, “type”: “circle”, “paint”: { “circle-radius”: 10,

Slide 35

Slide 35

this.map = map map.on(‘load’, () => { this.loaded = true map.addSource(‘point’, { “type”: “geojson”, “data”: { “type”: “Feature”, “properties”:{}, “geometry”: { “type”: “Point”, “coordinates”:[-71.51794373989105,42.23001485466201] } } }); map.addLayer({ “id”: “point”, “source”: “point”, “type”: “circle”, “paint”: { “circle-radius”: 10, “circle-color”: “#007cbf” } }); }) } } </script>

Slide 36

Slide 36

}) }, watch:{ loaded() { this.timestamp = performance.now() this.raf = requestAnimationFrame(animateMarker.bind(this)) function animateMarker() { let time = performance.now() //ms const duration = 6 * 1000 let speed = 5000 / duration //in m const path = turf.lineString(this.route.geometry.coordinat const timeElapsed = time - this.timestamp if (timeElapsed * speed >= 5000) { cancelAnimationFrame(this.raf) } else { var distTravelled = (timeElapsed * speed) var waypoint = turf.along(path, distTravelled, ‘meters’) this.map.getSource(‘point’).setData(waypoint) this.raf = requestAnimationFrame(animateMarker.bind(this)) } } } }, mounted() { var map = new mapboxgl.Map({

Slide 37

Slide 37

<template> <div class=”map—wrapper”> <div id=”map” ref=”map”></div> </div> </template> <script> import { watch, value, onMounted } from “vue-function-api”; const mapboxgl = require(‘mapbox-gl’); import useWaypoint from “../functions/useWaypoint”; export default { props: { data: Object }, setup(props, context) { let { movePoint } = useWaypoint(props.data); const mapInstance = value(null) const lineString = value(null) onCreated(() => { mapboxgl.accessToken = “API_KEY_HERE” fetch(“https://gist.githubusercontent.com/shortdiv/97d74.geojs .then(response => { return response.json();

Slide 38

Slide 38

<template> <div class=”map—wrapper”> <div id=”map” ref=”map”></div> </div> </template> <script> import { watch, value, onMounted } from “vue-function-api”; const mapboxgl = require(‘mapbox-gl’); import useWaypoint from “../functions/useWaypoint”; export default { props: { data: Object }, setup(props, context) { let { movePoint } = useWaypoint(props.data); const mapInstance = value(null) const lineString = value(null) onCreated(() => { mapboxgl.accessToken = “API_KEY_HERE” fetch(“https://gist.githubusercontent.com/shortdiv/97d74.geojs .then(response => { return response.json();

Slide 39

Slide 39

export default { props: { data: Object }, setup(props, context) { let { movePoint } = useWaypoint(props.data); const mapInstance = value(null) const lineString = value(null) onCreated(() => { mapboxgl.accessToken = “API_KEY_HERE” fetch(“https://gist.githubusercontent.com/shortdiv/97d74.geojs .then(response => { return response.json(); }) .then(data => { lineString.value = data }) }) onMounted(() => { var map = new mapboxgl.Map({ container: context.refs.map, style: ‘mapbox://styles/mapbox/dark-v10’, center: [-71.46933442476096,42.25342191415163], zoom: 12 });

Slide 40

Slide 40

}) onMounted(() => { var map = new mapboxgl.Map({ container: context.refs.map, style: ‘mapbox://styles/mapbox/dark-v10’, center: [-71.46933442476096,42.25342191415163], zoom: 12 }); map.on(‘load’, () => { mapInstance.value = map map.addSource(‘point’, { “type”: “geojson”, “data”: { “type”:”Feature”, “properties”:{}, “geometry”:{ “type”:”Point”, “coordinates”:[-71.51794373989105,42.23001485466201] } } }); map.addLayer({ “id”: “point”, “source”: “point”, “type”: “circle”, “paint”: { “circle-radius”: 10,

Slide 41

Slide 41

“type”:”Feature”, “properties”:{}, “geometry”:{ “type”:”Point”, “coordinates”:[-71.51794373989105,42.23001485466201] } } }); map.addLayer({ “id”: “point”, “source”: “point”, “type”: “circle”, “paint”: { “circle-radius”: 10, “circle-color”: “#007cbf” } }); }); watch(() => lineString.value, val => { moveMap(mapInstance.value) }) } } </script>

Slide 42

Slide 42

import { value, state } from “vue-function-api”; var turf = require(“turf”); export default function useWaypoint(route) { const movePoint = map => { const waypointVal = value({ type: “Feature”, geometry: { type: “Point”, coordinates: route.geometry.coordinates[0] } }); const waypointState = state({ timestamp: performance.now(), raf: null }); const animateMarker = () => { let time = performance.now(); //ms const duration = 6 * 1000; // 6000ms or 6s let speed = 5000 / duration; //in m const path = turf.lineString(route.geometry.coordinates); const timeElapsed = time - waypointState.timestamp; if (timeElapsed * speed >= 5000) { cancelAnimationFrame(waypointState.raf); } else { var distTravelled = timeElapsed * speed;

Slide 43

Slide 43

import { value, state } from “vue-function-api”; var turf = require(“turf”); export default function useWaypoint(route) { const movePoint = map => { const waypointVal = value({ type: “Feature”, geometry: { type: “Point”, coordinates: route.geometry.coordinates[0] } }); const timeStamp = value(performance.now()); const raf = value(null); const animateMarker = () => { let time = performance.now(); //ms const duration = 6 * 1000; // 6000ms or 6s let speed = 5000 / duration; //in m const path = turf.lineString(route.geometry.coordinates); const timeElapsed = time - timestamp.value; if (timeElapsed * speed >= 5000) { cancelAnimationFrame(waypointState.raf); } else { var distTravelled = timeElapsed * speed; waypointVal.value = turf.along(path, distTravelled, “meters”

Slide 44

Slide 44

const timeStamp = value(performance.now()); const raf = value(null); const animateMarker = () => { let time = performance.now(); //ms const duration = 6 * 1000; // 6000ms or 6s let speed = 5000 / duration; //in m const path = turf.lineString(route.geometry.coordinates); const timeElapsed = time - timestamp.value; if (timeElapsed * speed >= 5000) { cancelAnimationFrame(waypointState.raf); } else { var distTravelled = timeElapsed * speed; waypointVal.value = turf.along(path, distTravelled, “meters” map.getSource(‘point’).setData(waypointVal.value) raf.value = requestAnimationFrame( animateMarker.bind(this) ); } }; waypointState.raf = requestAnimationFrame( animateMarker.bind(this) ); } return { movePoint } }

Slide 45

Slide 45

<template> <div class=”map—wrapper”> <div id=”map” ref=”map”></div> </div> </template> <script> import { watch, value, onMounted } from “vue-function-api”; const mapboxgl = require(‘mapbox-gl’); import useWaypoint from “../functions/useWaypoint”; export default { props: { data: Object }, setup(props, context) { let { movePoint } = useWaypoint(props.data); const mapInstance = value(null) const lineString = value(null) onCreated(() => { mapboxgl.accessToken = “API_KEY_HERE” fetch(“https://gist.githubusercontent.com/shortdiv/97d74.geojs .then(response => { return response.json();

Slide 46

Slide 46

<template> <div class=”map—wrapper”> <div id=”map” ref=”map”></div> </div> </template> <script> import { watch, value, onMounted } from “vue-function-api”; const mapboxgl = require(‘mapbox-gl’); import useWaypoint from “../functions/useWaypoint”; export default { props: { data: Object }, setup(props, context) { let { movePoint } = useWaypoint(props.data); const mapInstance = value(null) const lineString = value(null) onCreated(() => { mapboxgl.accessToken = “API_KEY_HERE” fetch(“https://gist.githubusercontent.com/shortdiv/97d74.geojs .then(response => { return response.json();

Slide 47

Slide 47

“type”:”Point”, “coordinates”:[-71.51794373989105,42.23001485466201] } } }); map.addLayer({ “id”: “point”, “source”: “point”, “type”: “circle”, “paint”: { “circle-radius”: 10, “circle-color”: “#007cbf” } }); }); watch(() => lineString.value, val => { moveMap(mapInstance.value) }) } } </script>

Slide 48

Slide 48

Slide 49

Slide 49

Will stuff change in Vue3? Yes, Most Certainly

Slide 50

Slide 50

Vue 3 is coming for you