On ne traitera que du JS dans le navigateur
NodeJS est donc hors scope, même si on l'utilise de fait pour beaucoup de tâche de build
Callback: fonction passée à une autre fonction en vue d'être éxécutée plus tard lorsque le traitement asynchrone sera terminé
SPA (Single Page Application): application full javascript où le serveur HTTP ne sers qu'à distribuer une page HTML quasiment vide, à servir les assets, et à bootstraper l'application JS
NodeJS: Plateforme JS se reposant sur le moteur V8 de Google pour éxécuter du Javascript en dehors d'un contexte web (on parle souvent de Javascript côté serveur, mais npm sur votre machine exécute aussi NodeJS)
Transpileur: outils de transformation d'un language vers un autre language
Polyfills / shim: prothèse d'émulation, c'est à dire logiciel implémentant une fonctionnalité manquante dans des interfaces antérieures
Framework: cadre de développement, il fournit des librairies et des patterns
Librairies: brique applicative
Programmation réactive: paradigme de programmation visant à propager les modifications d'une source vers les éléments dépendants de cette source. (~wikipedia)
SourceMaps: principe de mapping des fichiers sources par rapport aux fichiers de production (bundelisé, transpilé, minifié). Il permet de faire du debug
{# twig #}
{% if pagination is defined %}
... HTML ...
<script>
$(document).ready(function () {
//on change submit
$('#paginationLimit').change(function () {
window.location.href = this.value;
});
});
</script>
... HTML ...
{% endif %}
{# twig #}
{{ form_start(filter, {'method': 'POST', 'attr': {'class': 'smart-form' }}) }}
... HTML ...
{% if searchMode is defined and searchMode == true %}
<script>
$(document).ready(function () {
var searchType = $('#ref_app_merger_search_type_searchType');
var csrfToken = $('#ref_app_merger_search_type__token');
var btn = $('button[type=submit]');
function generateFilters() {
// ... retrieve the corresponding form.
var form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[searchType.attr('name')] = searchType.val();
data[csrfToken.attr('name')] = csrfToken.val();
data['filter'] = true;
// Submit data via AJAX to the form's action path.
$.ajax({
url: form.attr('action'),
type: 'POST',
dataType: 'text',
data: data,
success: function (html) {
$('#filter').html(html);
},
error: function (xhr, status, error) {
//console.log(xhr);
}
});
}
// When searchType gets selected ...
searchType.change(function () {
generateFilters();
});
//when keyup on inputs
$('input:text').keyup(function () {
var disable = true;
//for each input
$('input:text').each(function () {
//test if input has value and for IE9 if the input value is not equal to the placeholder value
if ($(this).val() && $(this).val() != $(this).attr('placeholder')) {
disable = false;
}
});
//change disabled state of button search
$(btn).prop('disabled', disable);
});
});
</script>
{% else %}
<script>
$(document).ready(function () {
//on change submit
$('#ref_app_merger_mergerfilter_status').change(function () {
$(this).closest('form').submit();
});
});
</script>
{% endif %}
... HTML ...
... Et encore du JS ...
... Loop Over ...
Bref, vous faites du JQuery ! pas du Javascript
var name_1 = 'Benjamin'; // ne plus utiliser
let name_2 = 'François'; // disponible dans le scope de création: fonction/boucle/condition/globale,
// ne peut être recréé
const name_3 = 'Didier'; // idem let, mais non réaffectable
Scope: fin du hoisting
const txt = `merci ${maVar}, aujourd'hui il est
${(new Date()).toString()}, et 1 + 1 = ${1+1}`
// mieux que:
txt = 'merci ' + maVar + ', aujourd\'hui il est '\
(new Date()).toString() + ', et 1 + 1 = ' + (1+1)
Template String
'toto'.startsWith('to') // String.endsWith('to') also exists
String: nouvelles méthodes
[1, 2, 3, 4].includes(2)
Array: nouvelles méthodes
const getAge = function (age = 15) {
console.log(age)
}
getAge() // display 15
Function: default value
const getAge = (age = 15) => console.log(age) // fait un return de console.log(age) donc undefined
Function: Arrow function
let default = [1, 2, 1, 1, 3, 4, 5, 4, 3]
let set = new Set(default);
console.log(default.length); // 9
console.log(set.size); // 5
set.add(1)
console.log(set.size); // 5
console.log(set.has(5));
Set et Hash
Et plein d'autres choses: Promise, Spread operator, Async/Await ...
Faites un tour sur https://developer.mozilla.org/fr/docs/Web/API
pour découvrir toutes les nouvelles API disponibles ou expérimentales:
ES6+ c'est bien mais mon client il utilise du IE10, ou pire du Safari
Et bien ne vous privez pas, codez dans la dernière version d'Ecmascript et transpilez votre code dans une version compatible.
Babel = transpileur de code Javascript vers un code compatible Ecmascript 5
Attention, si le navigateur n'est pas compatible ES5 (IE9 n'a pas toutes les features), ajoutez des polyfills via core-js (inclus dans Babel)
Exemple: pour une cible IE9 avec utilisation de es6.array.for-each
// fichier vendor.js inclus dans toutes vos pages
// Don't use dynamic import to get Promise available immediatly
require ('core-js/modules/es6.promise')
// dynamic import from babel
import ('babel-polyfill')
// dynamic import for all required shim and polyfills from core-js ([].forEach not available in IE9)
import ('core-js/modules/es6.array.for-each.js')
Aujourd'hui on ne livre plus un gros fichier JS pour toute une application
Aujourd'hui on split son application JS en bundle par domaine métier
Webpack va nous aider à faire ces bundles (avant on utilisait Browserify ou d'autres outils, depuis peu ParcelJS tente de se faire une place grace à une simplicité d'utilisation)
Mais Webpack c'est pas si simple (avant la v4)
@Symfony/webpack-encore: pour simplifier notre vie (Laravel a le sien aussi)
C'est vite compliqué, et ça change souvent
var Encore = require('@symfony/webpack-encore')
Encore
// the project directory where compiled assets will be stored
.setOutputPath('public/dist/')
// the public path used by the web server to access the previous directory
.setPublicPath('/dist')
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
// uncomment to create hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel(function(babelConfig) {
// add additional presets
babelConfig.presets.push('es2017')
babelConfig.presets.push('react')
// no plugins are added by default, but you can add some
// babelConfig.plugins.push('styled-jsx/babel');
})
// this one was used for this npm package: offline-plugin to manage cache (angular has already its own worker since ng-5.2 & cli-1.6)
.addEntry('service-worker', './assets/js/lib/service-worker.js')
.addEntry('js/vuejs', './assets/js/vuejs/app.js')
.addEntry('js/login', './assets/js/login/app.js')
// for specific page css (not managed by vue file per example
.addStyleEntry('css/quasar-bootstrap', './assets/css/quasar-bootstrap.scss')
// this creates a 'vendor.js' file with common js code
.createSharedEntry('vendor', [
'./assets/js/app.js',
'./assets/css/app.scss',
])
.enableSassLoader(function(sassOptions) {}, {
resolveUrlLoader: false,
})
.enableVueLoader()
.enableReactPreset()
module.exports = config
Exemple
Demo
L'arrivée des frameworks comme AngularJS, EmberJS, React, Angular, Vue a introduit une notion intéressante en développement web: la programmation réactive (~les signaux avec Qt)
Dans l'exemple suivant l'input écoute les changements sur message et les applique à son attribut value
<template>
<div>
<p>{{ message }}</p>
<input v-model="message">
</div>
</template>
<script>
export default {
name: 'Text',
data: () => {
return {message: 'init'}
}
}
</script>
ouvrir le debugger Chrome
Linkedin est en full EmberJS 2 depuis 2016
Et la plupart des grands du web utilisent des SPA en interne ou sur le web
Pas de migration prévu pour le code existant, mais cohabitation de l'historique avec ES6+ et VueJS
Le choix d'un framework est généralement issue d'une réflexion biaisée par une idéologie. Celle des décideurs !
React (via CreateReactApp) vs VueJS vs Angular5
Ils sont presque tous pareil (même si c'est un brin provocateur)