removing old markdown notes
This commit is contained in:
83
links.md
83
links.md
@@ -1,83 +0,0 @@
|
|||||||
# Vue resources/references
|
|
||||||
|
|
||||||
## Javascript
|
|
||||||
|
|
||||||
Babel ES2015 guide (good overview to JS)
|
|
||||||
<https://babeljs.io/docs/en/learn>
|
|
||||||
|
|
||||||
## Interesting projects/things
|
|
||||||
|
|
||||||
### Strapi
|
|
||||||
|
|
||||||
<https://strapi.io/>
|
|
||||||
|
|
||||||
Strapi is a opensource headless CMS. You can self-host and use it as an admin page to generate content. You can then write a front-end, and have it consume your Strapi endpoints to display content.
|
|
||||||
|
|
||||||
A good tutorial to follow is:
|
|
||||||
|
|
||||||
<https://strapi.io/blog/build-a-blog-using-nuxt-strapi-and-apollo/>
|
|
||||||
|
|
||||||
This will use Nuxt and Strapi to create a blog.
|
|
||||||
|
|
||||||
### Pug
|
|
||||||
|
|
||||||
<https://itnext.io/pug-js-to-make-your-life-easier-with-html-templates-9c62273626e0?gi=1d44eb088155>
|
|
||||||
|
|
||||||
Pug is a HTML templating engine that lets you write simplified HTML which it will render into full html. It has additional features, like loops/condititons/includes to do fancier stuff than with pure HTML.
|
|
||||||
|
|
||||||
## Vuejs
|
|
||||||
|
|
||||||
### General (vue)
|
|
||||||
|
|
||||||
Awesome Vue
|
|
||||||
<https://github.com/ais-one/vue-crud-x>
|
|
||||||
|
|
||||||
### Extensions
|
|
||||||
|
|
||||||
Real time data-table editing in Vue:
|
|
||||||
<https://www.freecodecamp.org/news/how-to-build-a-real-time-editable-data-table-in-vue-js-46b7f0b11684/>
|
|
||||||
|
|
||||||
Integrating a datetime into a datatable CRUD:
|
|
||||||
<https://www.reddit.com/r/vuejs/comments/apdm4u/how_to_integrate_a_datepicker_like_this_in/>
|
|
||||||
|
|
||||||
Vuetify CRUD datatables:
|
|
||||||
<https://github.com/ais-one/vue-crud-x>
|
|
||||||
|
|
||||||
Loading bar component for top of page:
|
|
||||||
<https://medium.com/js-dojo/how-to-visualize-application-loading-state-in-vuetify-44f0f0242094>
|
|
||||||
<https://gist.github.com/AlexeyIsavnin/c24d7ae75bfdb599907bd36d6bfc0344>
|
|
||||||
|
|
||||||
### Tutorials/Guides (vue)
|
|
||||||
|
|
||||||
Using AJAX and rate limiting with lodash:
|
|
||||||
<https://vuejs.org/v2/guide/computed.html#Watchers>
|
|
||||||
|
|
||||||
### References
|
|
||||||
|
|
||||||
Composition API in Vue3:
|
|
||||||
<https://css-tricks.com/an-early-look-at-the-vue-3-composition-api-in-the-wild/?ref=madewithvuejs.com>
|
|
||||||
|
|
||||||
## Interesting designs
|
|
||||||
|
|
||||||
### Sites
|
|
||||||
|
|
||||||
- <https://binbytes.com>
|
|
||||||
|
|
||||||
### Fonts
|
|
||||||
|
|
||||||
- `Rene bieder campton`
|
|
||||||
|
|
||||||
## Nuxt
|
|
||||||
|
|
||||||
### General (nuxt)
|
|
||||||
|
|
||||||
<https://github.com/nuxt-community/awesome-nuxt>
|
|
||||||
|
|
||||||
### Tutorials/Guides (nuxt)
|
|
||||||
|
|
||||||
<https://strapi.io/blog/build-a-blog-using-nuxt-strapi-and-apollo/>
|
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
Good free HQ images:
|
|
||||||
<https://unsplash.com/s/photos/random>
|
|
||||||
456
notes.md
456
notes.md
@@ -1,456 +0,0 @@
|
|||||||
To Do:
|
|
||||||
|
|
||||||
Organise Trilium with the Vue development
|
|
||||||
|
|
||||||
- Have a note for tutorials
|
|
||||||
- Have a note for references (grouped by category)
|
|
||||||
|
|
||||||
Once done with this, go through this and the auth0 tutorial and pick out any vue/javascript snippets into their own trilium note.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
|
|
||||||
Vuetify
|
|
||||||
|
|
||||||
Forms
|
|
||||||
https://vuetifyjs.com/en/components/forms
|
|
||||||
|
|
||||||
To make a form you define a `v-model` of valid in the `<v-form>`. `lazy-validation` will set this to true unless there are visible validation errors.
|
|
||||||
|
|
||||||
`<v-form v-model="valid" ref="form" lazy-validation="">`
|
|
||||||
|
|
||||||
The `v-form` component has three functions that can be access by setting a `ref` on the component.
|
|
||||||
|
|
||||||
- this.\$refs.form.validate()
|
|
||||||
- this.\$refs.form.reset()
|
|
||||||
- this.\$refs.form.resetValidation()
|
|
||||||
|
|
||||||
Validation is easy: https://vuetifyjs.com/en/components/forms/#usage
|
|
||||||
|
|
||||||
Custom rules can be done: https://vuetifyjs.com/en/components/forms/#creating-rules
|
|
||||||
|
|
||||||
When the `submit()` event is made, `this.$refs.form.validate()` will check the form.
|
|
||||||
|
|
||||||
`v-model` is used to bind data 2 ways to the form and the data instance in vue.
|
|
||||||
|
|
||||||
https://vuejs.org/v2/guide/forms.html
|
|
||||||
|
|
||||||
`v-text-fields` are used to render inputs in a form.
|
|
||||||
|
|
||||||
https://vuetifyjs.com/en/components/text-fields/
|
|
||||||
|
|
||||||
Using `:` before a prop means that the prop is dynamic and the value of it contains javascript.
|
|
||||||
|
|
||||||
`:append-icon="passwordVisible ? 'visibility' : 'visibility_off'"`
|
|
||||||
|
|
||||||
Here this is saying that `append-icon` takes a dynamic value. We are using the `? :` syntax - conditional ternary operator. This is short for a if then else.
|
|
||||||
|
|
||||||
Available icons are (with `mdi-` prefix) https://materialdesignicons.com/ or https://material.io/resources/icons/?style=baseline
|
|
||||||
|
|
||||||
Double bang notation
|
|
||||||
|
|
||||||
The notation `!!variable` is double bang notation
|
|
||||||
https://medium.com/better-programming/javascript-bang-bang-i-shot-you-down-use-of-double-bangs-in-javascript-7c9d94446054
|
|
||||||
|
|
||||||
It returns the `variable` _truthy_ value. I.e if it's a string with a value it will be true. This is similar to doing `variable.__bool__` in python.
|
|
||||||
|
|
||||||
When writing inputs you can create custom rules.
|
|
||||||
|
|
||||||
For example: (in the `computed`)
|
|
||||||
|
|
||||||
```
|
|
||||||
passwordRules() {
|
|
||||||
return {
|
|
||||||
required: value => !!value || 'Required.',
|
|
||||||
min: v => v.length >= 8 || 'Min 8 characters',
|
|
||||||
emailMatch: () => ('The email and password you entered don\'t match'),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
For a `<v-text-field`:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<v-text-field
|
|
||||||
v-model="password"
|
|
||||||
:append-icon="passwordVisible ? 'visibility' : 'visibility_off'"
|
|
||||||
:rules="[passwordRules.required, passwordRules.min]"
|
|
||||||
>
|
|
||||||
</v-text-field>
|
|
||||||
```
|
|
||||||
|
|
||||||
The value of the field will be passed to the method `passwordRules()` and passed into the function inside it. This way, we can verify that the value exists (using double bang notation) and that it has a minimum length.
|
|
||||||
|
|
||||||
Events on the input can be defined using the following syntax:
|
|
||||||
`@click:append="passwordVisible = !passwordVisible"`
|
|
||||||
This event (each event is documented in the Vuetify docs) will trigger when you click on the append icon you defined.
|
|
||||||
|
|
||||||
You can write regex in javascript by placing it between two slash's.
|
|
||||||
They are useful with arrow notation to check if something matches a regex.
|
|
||||||
`v => /.+@.+/.test(v) || 'E-mail must be valid.'`
|
|
||||||
|
|
||||||
Backtick strings is javascript's version of f-strings in python. You can then access variables with `${}` notation:
|
|
||||||
|
|
||||||
```
|
|
||||||
console.log(
|
|
||||||
`SIGN UP username: ${this.username}, password: ${this.password}, email: ${this.username}`
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can write an if block like you would a function:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
if (this.$refs.form.validate()) {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also follow this with an else (aligned with the end of the if block)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
if (condition) {
|
|
||||||
// block of code to be executed if the condition is true
|
|
||||||
} else {
|
|
||||||
// block of code to be executed if the condition is false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use the conditional ternary notation of
|
|
||||||
`condition ? true_action : false_action`
|
|
||||||
|
|
||||||
Computed (properties and watchers)
|
|
||||||
https://stackoverflow.com/a/44350932
|
|
||||||
|
|
||||||
With computed you are creating getters/setters. You do this by writing the function in the `computed` directive.
|
|
||||||
Here we have written a computed property for `emailRules()` - we are using this function to check input on a form is correct.
|
|
||||||
|
|
||||||
Computed properties don't take any arguments - but you can access the `data` scope through `$this.` if you need access to the data in the scope.
|
|
||||||
|
|
||||||
They are better than using watchers/methods if you don't need the extra functionality. A good example is https://michaelnthiessen.com/most-important-feature-vue/
|
|
||||||
|
|
||||||
The main difference between `computed` and a `method` is that `computed` will cache automatically. It will only update, if the value updates. Everytime you call it it will use its cache. Expensive operations can be done this way.
|
|
||||||
|
|
||||||
They are properties (like in Python). Computed properties will update whenever one of the values needed to make it is also updated. Whenever you want to filter or transform your data, you can use a Computed property.
|
|
||||||
|
|
||||||
E.g if you have a list of names, and you want just those names that begin with 'b', you can write it as a computed property that returns this list. The stack overflow answer above demonstrates this.
|
|
||||||
|
|
||||||
Remember: anything in `data` you can access in the `<script>` with `$this.var`. In the html you can access the var name directly `{{var}}`
|
|
||||||
|
|
||||||
Watchers
|
|
||||||
https://vuejs.org/v2/guide/computed.html#Watchers
|
|
||||||
|
|
||||||
Watchers are like computed but they are a more generic way to watch for datachanges. If you need to add more functionality (rate limiting) or refer to many methods or take many arguments - a watcher might be more appropiate.
|
|
||||||
|
|
||||||
The watch directive can be used to watch variables in the component. You create a function that is the same name as the variable you want to watch. This function should take two arguments: new and old. (You can use this to compare inputs for exmaple).
|
|
||||||
|
|
||||||
There is a good example of using this functionality to do an ajax call (with rate limiting) in the documentation for watchers above.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Sending form data to an api
|
|
||||||
|
|
||||||
You can check the form is in a valid state by doing `this.$refs.form.validate()`
|
|
||||||
|
|
||||||
You can then access each component of the form to the variable you defined in the `v-model` attribute.
|
|
||||||
|
|
||||||
`<v-text-field v-model="username" />` can be accessed with `this.username`.
|
|
||||||
|
|
||||||
Remember that the variable defined in the `v-model` has to be defined in the `data`.
|
|
||||||
|
|
||||||
|
|
||||||
Usage of `$this`
|
|
||||||
|
|
||||||
The following (under the "No separate this" header) explains well.
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
|
||||||
|
|
||||||
When you write a function and define a variable, you might want to write another function inside. If you use `$this` inside the second function, you will find it is bound to the global scope, not the parent function!
|
|
||||||
|
|
||||||
To fix this behaviour, you should _close over_ the variable `$this`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function Person() {
|
|
||||||
var that = this;
|
|
||||||
that.age = 0;
|
|
||||||
|
|
||||||
setInterval(function growUp() {
|
|
||||||
// The callback refers to the `that` variable of which
|
|
||||||
// the value is the expected object.
|
|
||||||
that.age++;
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is similar to "closure" in python.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Events system and having functions as props
|
|
||||||
https://michaelnthiessen.com/pass-function-as-prop/
|
|
||||||
https://vuejs.org/v2/guide/events.html
|
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
<!-- Parent -->
|
|
||||||
<template>
|
|
||||||
<ChildComponent @send-message="handleSendMessage" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
// Parent
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
handleSendMessage(event, value) {
|
|
||||||
// Our event handler gets the event, as well as any
|
|
||||||
// arguments the child passes to the event
|
|
||||||
console.log('From the child:', value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And in the child component we emit the event:
|
|
||||||
```javascript
|
|
||||||
// Child
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
method: { type: Function },
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return { value: 'I am the child.' };
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// Instead of calling the method we emit an event
|
|
||||||
this.$emit('send-message', this.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When the child component is mounted, it emits the event 'send-message'. The parent can then listen for this 'send-message' event and call a corresponding function. As the data flows in only one way, it makes for much easier debugging too. We can also pass up variables from the child to the parent in this way.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Object destructuring
|
|
||||||
|
|
||||||
When you define a function and specifiy attributes, you can use javascripts object destructing.
|
|
||||||
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
|
|
||||||
|
|
||||||
For example, if you write a function that takes a dictionary, but in the function you only need one of the key-value pairs and not the rest, you can write your function arguments to take this using `{}` inside the arguments. Then when you pass in the whole dictionary as an argument to the function, it will only use what you've told it to.
|
|
||||||
|
|
||||||
Listening to events
|
|
||||||
|
|
||||||
In a parent, you can use the `@` syntax on a html tag to listen to an event.
|
|
||||||
`<ChildComponent @send-message="handleSendMessage" />`
|
|
||||||
|
|
||||||
This will listen for a `send-message` event from this child, and when it receives it it will run the function handleSendMessage.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
mounted() {
|
|
||||||
this.$emit('send-message', this.value);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
This will trigger the handler in the parent, and pass the `this.value` parameter up with it.
|
|
||||||
|
|
||||||
|
|
||||||
Slots
|
|
||||||
https://vuejs.org/v2/guide/components-slots.html
|
|
||||||
|
|
||||||
Slots are useful if you want to send data from a parent down to a child (or vice versa) and want to include more advanced things.
|
|
||||||
|
|
||||||
For example, you can use a slot to send html from a parent down to a child to be rendered.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// parent
|
|
||||||
<navigation-link url="/profile">
|
|
||||||
<!-- Add a Font Awesome icon -->
|
|
||||||
<span class="fa fa-user"></span>
|
|
||||||
Your Profile
|
|
||||||
</navigation-link>
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// child (navigation-link)
|
|
||||||
<a
|
|
||||||
v-bind:href="url"
|
|
||||||
class="nav-link"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</a>
|
|
||||||
```
|
|
||||||
|
|
||||||
The content between the `<slot>` will be replaced by what is between the `<navigation-link>`.
|
|
||||||
|
|
||||||
Summary:
|
|
||||||
|
|
||||||
Send data
|
|
||||||
|
|
||||||
From parent to child: use props.
|
|
||||||
From child to parent: use events.
|
|
||||||
|
|
||||||
|
|
||||||
Send data parent to child:
|
|
||||||
If you want to send data from a parent to a child, but the data is defined on the child but you want to send a reference to it from the parent then have the parent send an arrow function as a prop, which returns the statement/expression you want to be rednered in the child.
|
|
||||||
|
|
||||||
As its a function, you can have it accept an argument. In the child you would refer to the prop name and pass in the arguments.
|
|
||||||
|
|
||||||
In the child:
|
|
||||||
`{ prop-name(argument)}`
|
|
||||||
|
|
||||||
|
|
||||||
Scoped Slots
|
|
||||||
https://www.youtube.com/watch?v=GWdOucfAzTo
|
|
||||||
|
|
||||||
If you want to send more than a string down, and want to send more advanced things like whole functions or even html, then you can use a scoped slot.
|
|
||||||
|
|
||||||
A scoped slot is a special kind of slot designed for this purpose. They are really powerful in Vue, and using them in design patterns allows you to decouple components.
|
|
||||||
|
|
||||||
To use a scoped slot you define a normal slot, but you pass add attributes to the slot tag in the html.
|
|
||||||
|
|
||||||
`<slot :attr1="param"></slot>`
|
|
||||||
|
|
||||||
You are passing in a key-value pair. This is slightly different to when you use arrow functions with arguments to render the data (unless your arrow function takes an object of a dictionary with the same key-value pairs).
|
|
||||||
|
|
||||||
So if we need to send data from a child to a parent:
|
|
||||||
|
|
||||||
In the child: write a `<scope :something="a value">` that takes arguments.
|
|
||||||
In the parent: write a `<template>` directive that has a `slot-scope` attribute on it: `<template slot-scope="name"> {{name.something}} </template>`.
|
|
||||||
|
|
||||||
You don't have to use a template, you can use any html tag - you just need some html tag to accept the `slot-scope` - (an `<a>` for example). You can fall back on template if needed as a default.
|
|
||||||
|
|
||||||
Think of a scoped slot as a function that returns template data.
|
|
||||||
Regular slots are just regular props.
|
|
||||||
Scoped slots are function props.
|
|
||||||
|
|
||||||
The video from adam linked above has a really good demonstration of this.
|
|
||||||
|
|
||||||
|
|
||||||
Portals
|
|
||||||
https://www.youtube.com/watch?v=1yWAxrpL3zU&list=PL7CcGwsqRpSOZAiNYyVvgTKSyARERvvij
|
|
||||||
|
|
||||||
Portals are useful if you want to render something in a child somewhere in a specific place in the DOM.
|
|
||||||
|
|
||||||
This is useful if you have a child component somewhere that is toggling a modal, and you want that model to actually be placed in the parent in a specific place in the DOM for formatting.
|
|
||||||
|
|
||||||
Portals will literally render the data where it needs to, then transport it to wherever you want it to actually be placed in the HTML.
|
|
||||||
|
|
||||||
|
|
||||||
Advanced Vue Component Design course
|
|
||||||
https://adamwathan.me/advanced-vue-component-design/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## javascript
|
|
||||||
|
|
||||||
### new
|
|
||||||
|
|
||||||
Javascript isn't like python where you can map an object to something else using ducktyping. In javascript if you want to map an object to something else, you need to use `new`.
|
|
||||||
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function Car(make, model, year) {
|
|
||||||
this.make = make;
|
|
||||||
this.model = model;
|
|
||||||
this.year = year;
|
|
||||||
}
|
|
||||||
|
|
||||||
const car1 = new Car('Eagle', 'Talon TSi', 1993);
|
|
||||||
|
|
||||||
console.log(car1.make);
|
|
||||||
// expected output: "Eagle"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding custom CSS site-wide
|
|
||||||
https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/
|
|
||||||
https://joshuatz.com/posts/2019/vue-mixing-sass-with-scss-with-vuetify-as-an-example/#easy-solution
|
|
||||||
|
|
||||||
Custom css (to apply a font for example) can be done so you don't need to import a `css` file in each component.
|
|
||||||
|
|
||||||
All fonts should go in `./src/assets/fonts`.
|
|
||||||
|
|
||||||
1. Create `./src/scss/_variables.scss`
|
|
||||||
2. In here create any custom css you need
|
|
||||||
|
|
||||||
```scss
|
|
||||||
@font-face {
|
|
||||||
font-family: "Campton";
|
|
||||||
src: url("~@/assets/fonts/Rene_Bieder-Campton_Medium.otf") format("opentype");
|
|
||||||
}
|
|
||||||
|
|
||||||
$my-font-family: "Campton", sans-serif !default;
|
|
||||||
|
|
||||||
@import "~vuetify/src/styles/styles.sass";
|
|
||||||
$body-font-family: "Times New Roman", Times, serif;
|
|
||||||
|
|
||||||
@import "~vuetify/src/styles/settings/variables";
|
|
||||||
$body-font-family: "Times New Roman", Times, serif;
|
|
||||||
|
|
||||||
$mycol: purple;
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Create `vue.config.js` in the root of the project (next to `package.json`).
|
|
||||||
4. Add the following to compile custom css:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
module.exports = {
|
|
||||||
css: {
|
|
||||||
loaderOptions: {
|
|
||||||
scss: {
|
|
||||||
// sassOptions: { indentedSyntax: true },
|
|
||||||
prependData: `
|
|
||||||
@import "@/scss/_variables.scss";
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transpileDependencies: ["vuetify"]
|
|
||||||
};
|
|
||||||
```
|
|
||||||
5. In `App.vue` you can set the font for the whole project.
|
|
||||||
|
|
||||||
```scss
|
|
||||||
#app {
|
|
||||||
font-family: "Campton", sans-serif, Arial, Helvetica,;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
color: $mycol;
|
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Import shortcuts using `@`
|
|
||||||
|
|
||||||
Both `~@` and `@` resolve to `./src`.
|
|
||||||
|
|
||||||
You use `~@` when in Vue components/js when set up with the vue cli.
|
|
||||||
|
|
||||||
You use `@` when in `scss`/`css` files - it's provided by webpack.
|
|
||||||
|
|
||||||
|
|
||||||
## Import and syntax
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
|
||||||
|
|
||||||
https://vueschool.io/articles/vuejs-tutorials/lazy-loading-and-code-splitting-in-vue-js/
|
|
||||||
|
|
||||||
### Importing js
|
|
||||||
|
|
||||||
In javascript you can use several ways to import modules.
|
|
||||||
|
|
||||||
`import { function } from "module";` is used if you want to import a specific function/class etc from a `.js` file. You can chain the imports with a comma between them. These have to be javascript objects.
|
|
||||||
|
|
||||||
`import defaultExport from "module";` is used to import the default exports from a `.js` file. The name you give it you will use to reference it (like doing `import module as name` in python).
|
|
||||||
|
|
||||||
You can also use lazy loading - only loading the components/modules you need when you need them. You can see this being used in the `./src/router/index.js`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "signup" */ "../views/SignUp.vue")
|
|
||||||
```
|
|
||||||
|
|
||||||
The difference between `require` and `import`: `import` is better to use as it's a function, whereas `require` is a js library. `require` is synchronous but `import` can be asynchronous, and supports lazy loading.
|
|
||||||
|
|
||||||
### Importing vue components
|
|
||||||
|
|
||||||
When importing other vue components (e.g vue-lottie) any package that says use `npm install --save` can be substituted with `yarn add`. The `--save` just says to add to the `package.json`.
|
|
||||||
|
|
||||||
If importing vue components that have been downloaded into `node_modules`, you can simply import them like any other component. The `.vue` is optional. e.g.
|
|
||||||
|
|
||||||
Installing is done with `yarn add vue-lottie` and importing in another vue component is done with `import Lottie from "vue-lottie"`.
|
|
||||||
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
# Managing sign in/out + displaying name
|
|
||||||
|
|
||||||
## Create the Vuex store
|
|
||||||
|
|
||||||
### state
|
|
||||||
|
|
||||||
Your state should contain 3 items:
|
|
||||||
|
|
||||||
- authorized:bool = flag if logged in or not.
|
|
||||||
- user: str = user object.
|
|
||||||
- userEmail: str = username or email.
|
|
||||||
|
|
||||||
### mutations
|
|
||||||
|
|
||||||
You should define two mutations (to update this state):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
mutations: {
|
|
||||||
user(state, user) {
|
|
||||||
state.authorized =
|
|
||||||
!!user && user.attributes && user.attributes.email_verified;
|
|
||||||
state.user = user;
|
|
||||||
},
|
|
||||||
userEmail(state, userEmail) {
|
|
||||||
state.userEmail = userEmail;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
- When you set the user state you should check to make sure the user object you pass in exists and set the authorized flag at the same time.
|
|
||||||
|
|
||||||
### actions
|
|
||||||
|
|
||||||
You should define an action that fetches the user. This should call the `getUser` method of whatever auth system you're using and pass this in to the method.
|
|
||||||
|
|
||||||
This `getUser` dispatch should either get the user and set the store state, or set it to null if no user exists. This way, any interaction with `getUser` will make sure the store is up to date.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
actions: {
|
|
||||||
async fetchUser({ commit }) {
|
|
||||||
try {
|
|
||||||
const user = await Auth.currentAuthenticatedUser();
|
|
||||||
const expires =
|
|
||||||
user.getSignInUserSession().getIdToken().payload.exp -
|
|
||||||
Math.floor(new Date().getTime() / 1000);
|
|
||||||
console.log(`Token expires in ${expires} seconds.`);
|
|
||||||
commit("user", user);
|
|
||||||
commit("userEmail", user.attributes.email);
|
|
||||||
} catch (err) {
|
|
||||||
commit("user", null);
|
|
||||||
commit("userEmail", null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
### getters
|
|
||||||
|
|
||||||
You should define getters to access the state. These getters will cache - so they will only update if one of the things used to calculate the state updates.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
getters: {
|
|
||||||
userEmail: state => {
|
|
||||||
return state.userEmail;
|
|
||||||
},
|
|
||||||
authorized: state => {
|
|
||||||
return state.authorized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Triggering state for apps
|
|
||||||
|
|
||||||
In addition to getting the user to determine router actions, you should get the user in the main component so you can set the state.
|
|
||||||
|
|
||||||
In the main view (parent of all components `App.vue`) you should define a `created` method. This method should:
|
|
||||||
|
|
||||||
- Fetch the user.
|
|
||||||
- Change the state to reflect this.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async created() {
|
|
||||||
try {
|
|
||||||
await this.$store.dispatch("fetchUser");
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
console.log(this.email);
|
|
||||||
console.log(this.$store.state.userEmail);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
This needs to be done right at the start to make sure this triggers a state change before anything else.
|
|
||||||
|
|
||||||
In the views for signing in/out/registering etc you should make sure that when you call the auth methods you wrote, that you also update the state at this time.
|
|
||||||
This way, any component in the app can use the state to determine if someone is logged in, and display/toggle components dynamically.
|
|
||||||
|
|
||||||
A signin method for a button might look like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async submit() {
|
|
||||||
if (this.$refs.form.validate()) {
|
|
||||||
console.log(
|
|
||||||
`SIGN IN username: ${this.username}, password: ${this.password}`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await signIn(this.username, this.password);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
} finally {
|
|
||||||
this.$store.dispatch("fetchUser");
|
|
||||||
}
|
|
||||||
console.log("Signed in");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A sign out button might look like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async dosignOut() {
|
|
||||||
try {
|
|
||||||
await signOut();
|
|
||||||
} finally {
|
|
||||||
this.$store.dispatch("fetchUser");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember:
|
|
||||||
|
|
||||||
- Call `this.$store.dispatch("fetchUser)` when you sign in - do it in a try/finally block to make sure the state gets updated after the ajax call.
|
|
||||||
- Call `this.$store.dispatch("fetchUser)` when you sign out do it in a try/finally bock to make sure the state gets updated after the ajax call.
|
|
||||||
282
tutorial.md
282
tutorial.md
@@ -1,282 +0,0 @@
|
|||||||
# AWS amplify
|
|
||||||
|
|
||||||
## Example repo
|
|
||||||
|
|
||||||
Use the following repo as an example/baseline:
|
|
||||||
|
|
||||||
<https://git.panaetius.co.uk/web-development/medium-aws-tutorial-1>
|
|
||||||
|
|
||||||
## Installaing amplify
|
|
||||||
|
|
||||||
Install amplify globally
|
|
||||||
|
|
||||||
`yarn global add @aws-amplify/cli`
|
|
||||||
|
|
||||||
Create new amplify app in project
|
|
||||||
|
|
||||||
`amplify init`
|
|
||||||
|
|
||||||
### Amplify commands
|
|
||||||
|
|
||||||
Amplify commands after init
|
|
||||||
|
|
||||||
`amplify status` - will show you what you've added already and if it's locally configured or deployed
|
|
||||||
`amplify add <category>` - will allow you to add features like user login or a backend API
|
|
||||||
`amplify push` - will build all your local backend resources and provision it in the cloud
|
|
||||||
`amplify console` - to open the Amplify Console and view your project status
|
|
||||||
`amplify publish` - will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
|
|
||||||
|
|
||||||
`amplify add api` - to create a backend API and then
|
|
||||||
`amplify publish` - to deploy everything
|
|
||||||
|
|
||||||
Install aws amplify dependencies into project
|
|
||||||
`yarn add aws-amplify aws-amplify-vue`
|
|
||||||
|
|
||||||
## Create auth flow
|
|
||||||
|
|
||||||
Add the Amplify instance to Vue instance in `main.js`
|
|
||||||
|
|
||||||
### Auth.js
|
|
||||||
|
|
||||||
Create a new js file in `./src/utils/auth.js` - write the aws amplify code in here.
|
|
||||||
|
|
||||||
You need to write a function for each auth flow. Signup, GetUser etc.
|
|
||||||
|
|
||||||
Remember to use
|
|
||||||
`export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut}`
|
|
||||||
at the end so it can be imported later.
|
|
||||||
|
|
||||||
You should write a view for each action, login, signup etc.
|
|
||||||
These views should define the UI, and also call the function in the `auth.js`. E.g if you need to sign in, you should write the UI, write any computed methods to verify the form, and write a submit method that takes the form details and calls the `signIn` function from the `auth.js` file.
|
|
||||||
|
|
||||||
In `./utils/auth.js` you can write the functions needed to actually log in with amplify.
|
|
||||||
|
|
||||||
For example, to signUp you can do:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function signUp(username, password) {
|
|
||||||
return Auth.signUp({
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
attributes: {
|
|
||||||
email: username
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
AmplifyEventBus.$emit("localUser", data.user);
|
|
||||||
if (data.userConfirmed === false) {
|
|
||||||
AmplifyEventBus.$emit("authState", "confirmSignUp");
|
|
||||||
} else {
|
|
||||||
AmplifyEventBus.$emit("authState", "signIn");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### router/index.js
|
|
||||||
|
|
||||||
In the `./router/index.js` you should write the router flow.
|
|
||||||
|
|
||||||
#### Define routes
|
|
||||||
|
|
||||||
Each route should set whether they need authentication or not.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
name: "Home",
|
|
||||||
component: Home,
|
|
||||||
meta: { requiresAuth: true }
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Additional functionality
|
|
||||||
|
|
||||||
In this file, you can write functions that control the behaviour. For example, you can force anyone logged in to go to the home page only:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
getUser().then(user => {
|
|
||||||
if (user) {
|
|
||||||
router.push({ path: "/" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Respond to events
|
|
||||||
|
|
||||||
AWS Amplify has the ability to natively `emit` events in your auth flow functions. When you emit these, you can use `AmplifyEventBus` to do something.
|
|
||||||
|
|
||||||
We can push people to different routes based on the sign in flow.
|
|
||||||
|
|
||||||
An example emit:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
AmplifyEventBus.$emit("authState", "signedIn");
|
|
||||||
```
|
|
||||||
|
|
||||||
An example listener in the router:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
AmplifyEventBus.$on("authState", async state => {
|
|
||||||
const pushPathes = {
|
|
||||||
signedOut: () => {
|
|
||||||
router.push({ path: "/signIn" });
|
|
||||||
},
|
|
||||||
signUp: () => {
|
|
||||||
router.push({ path: "/signUp" });
|
|
||||||
},
|
|
||||||
confirmSignUp: () => {
|
|
||||||
router.push({ path: "/signUpConfirm" });
|
|
||||||
},
|
|
||||||
signIn: () => {
|
|
||||||
router.push({ path: "/signIn" });
|
|
||||||
},
|
|
||||||
signedIn: () => {
|
|
||||||
router.push({ path: "/" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (typeof pushPathes[state] === "function") {
|
|
||||||
pushPathes[state]();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Managing sign in/out + displaying name
|
|
||||||
|
|
||||||
### Create the Vuex store
|
|
||||||
|
|
||||||
#### state
|
|
||||||
|
|
||||||
Your state should contain 3 items:
|
|
||||||
|
|
||||||
- authorized:bool = flag if logged in or not.
|
|
||||||
- user: str = user object.
|
|
||||||
- userEmail: str = username or email.
|
|
||||||
|
|
||||||
#### mutations
|
|
||||||
|
|
||||||
You should define two mutations (to update this state):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
mutations: {
|
|
||||||
user(state, user) {
|
|
||||||
state.authorized =
|
|
||||||
!!user && user.attributes && user.attributes.email_verified;
|
|
||||||
state.user = user;
|
|
||||||
},
|
|
||||||
userEmail(state, userEmail) {
|
|
||||||
state.userEmail = userEmail;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
- When you set the user state you should check to make sure the user object you pass in exists and set the authorized flag at the same time.
|
|
||||||
|
|
||||||
#### actions
|
|
||||||
|
|
||||||
You should define an action that fetches the user. This should call the `getUser` method of whatever auth system you're using and pass this in to the method.
|
|
||||||
|
|
||||||
This `getUser` dispatch should either get the user and set the store state, or set it to null if no user exists. This way, any interaction with `getUser` will make sure the store is up to date.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
actions: {
|
|
||||||
async fetchUser({ commit }) {
|
|
||||||
try {
|
|
||||||
const user = await Auth.currentAuthenticatedUser();
|
|
||||||
const expires =
|
|
||||||
user.getSignInUserSession().getIdToken().payload.exp -
|
|
||||||
Math.floor(new Date().getTime() / 1000);
|
|
||||||
console.log(`Token expires in ${expires} seconds.`);
|
|
||||||
commit("user", user);
|
|
||||||
commit("userEmail", user.attributes.email);
|
|
||||||
} catch (err) {
|
|
||||||
commit("user", null);
|
|
||||||
commit("userEmail", null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
#### getters
|
|
||||||
|
|
||||||
You should define getters to access the state. These getters will cache - so they will only update if one of the things used to calculate the state updates.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
getters: {
|
|
||||||
userEmail: state => {
|
|
||||||
return state.userEmail;
|
|
||||||
},
|
|
||||||
authorized: state => {
|
|
||||||
return state.authorized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Triggering state for apps
|
|
||||||
|
|
||||||
In addition to getting the user to determine router actions, you should get the user in the main component so you can set the state.
|
|
||||||
|
|
||||||
In the main view (parent of all components `App.vue`) you should define a `created` method. This method should:
|
|
||||||
|
|
||||||
- Fetch the user.
|
|
||||||
- Change the state to reflect this.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async created() {
|
|
||||||
try {
|
|
||||||
await this.$store.dispatch("fetchUser");
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
console.log(this.email);
|
|
||||||
console.log(this.$store.state.userEmail);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
This needs to be done right at the start to make sure this triggers a state change before anything else.
|
|
||||||
|
|
||||||
In the views for signing in/out/registering etc you should make sure that when you call the auth methods you wrote, that you also update the state at this time.
|
|
||||||
This way, any component in the app can use the state to determine if someone is logged in, and display/toggle components dynamically.
|
|
||||||
|
|
||||||
A signin method for a button might look like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async submit() {
|
|
||||||
if (this.$refs.form.validate()) {
|
|
||||||
console.log(
|
|
||||||
`SIGN IN username: ${this.username}, password: ${this.password}`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await signIn(this.username, this.password);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
} finally {
|
|
||||||
this.$store.dispatch("fetchUser");
|
|
||||||
}
|
|
||||||
console.log("Signed in");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A sign out button might look like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async dosignOut() {
|
|
||||||
try {
|
|
||||||
await signOut();
|
|
||||||
} finally {
|
|
||||||
this.$store.dispatch("fetchUser");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember:
|
|
||||||
|
|
||||||
- Call `this.$store.dispatch("fetchUser)` when you sign in - do it in a try/finally block to make sure the state gets updated after the ajax call.
|
|
||||||
- Call `this.$store.dispatch("fetchUser)` when you sign out do it in a try/finally bock to make sure the state gets updated after the ajax call.
|
|
||||||
43
vuex.md
43
vuex.md
@@ -1,43 +0,0 @@
|
|||||||
# Vuex
|
|
||||||
|
|
||||||
<https://vuex.vuejs.org/>
|
|
||||||
|
|
||||||
In `./store/index.js` you can define your state. For simple apps you can leverage this single file. For anything more advanced you should use modules.
|
|
||||||
<https://vuex.vuejs.org/guide/modules.html>
|
|
||||||
|
|
||||||
## state
|
|
||||||
|
|
||||||
The state serves as a single source of truth for what you need. You typically have only one store for each application, but you can break it down into modules if needed.
|
|
||||||
|
|
||||||
You declare your variables you need in the state. To affect the state, you do `mutations` and `actions`.
|
|
||||||
|
|
||||||
## getters
|
|
||||||
|
|
||||||
Getters are like computed propeties for the state. If you needed derived properties of what's in the state - you can define a getter for this purpose. For example, if you have a list in the state you can define a getter that returns the length of that list. This is useful because you might need to do this in more than one Vue component - duplicating code. Using a getter means you can access it from the store.
|
|
||||||
|
|
||||||
You can do any function in getters - more complicated actions as properties reduces the amount of duplicated code across components.
|
|
||||||
|
|
||||||
## mutations
|
|
||||||
|
|
||||||
To change the state you need to use a mutation. Each mutation has a string type and a handler. The handler function is where we peform state modifications and it will receive the state as its first argument.
|
|
||||||
|
|
||||||
You cannot call a mutation handler directly - think of them like event registration. To call it, you must do a `store.commit('handlername')`.
|
|
||||||
|
|
||||||
You can pass a payload to a mutation, write the mutation to take state, and any other arguments you need. Then do a `store.commit('handlername', arg1)`.
|
|
||||||
|
|
||||||
## actions
|
|
||||||
|
|
||||||
An action is a function that you can use to manipulate the state. Since mutations have to be _synchronous_, you can use an action if you need _asynchronous_ operations.
|
|
||||||
|
|
||||||
An action handler receives a `context` object which exposes the same set of methods/properties on the store instance. So you can use `context.commit` to commit a mutation or access the state and getters with `context.state` and `context.getters`. You can call other actions with `context.dispatch`.
|
|
||||||
|
|
||||||
You can use arugment destructuring to write cleaner code, rather than accepting the entire context object:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
actions = {
|
|
||||||
async login({dispatch, state}, {email, password}){
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here this handler only needs to do `context.dispatch` and `context.state`. The `{email, password}` will come from the user object we pass as an argument. Inside this handler we can then access any of the state functions we need to do.
|
|
||||||
Reference in New Issue
Block a user