Translate

Поиск по этому блогу

пятница, 7 сентября 2018 г.

ES6 Spread Operator-оператор разворота (IV).

Оператор разворота (spread operator).





Все материалы по ES6


Оператор разворота или распространения - spread operator выглядит как три точки ...

Он позволяет разворачивать элементы массива для передачи их в качестве аргументов функции, или в элементы другого массива.

Лучше всего это объяснить на примере.



У нас есть два массива и мы хотим перенести элементы первого массива во воторй. Для этого мы можем использовать оператор разворота, который развернет первый массив ...arr1 и вставит их во второй - см. картинку выше.

Тепреь представьте, что у нас есть некий массив из трех элементов. И есть функция, которая принимает именно три аргумента.



Сам массив отправить в функцию мы не можем. но мы можем развернуть массив в аргументы функции используя оператор разворота.

doSomething(...arr1);





Теперь попробуем применить оператор разворота на практике.



Первое что мы сделаем это запустим скрипт Babel, который будет следить за файлами, и при их изменении создавать свежие версии.

Откроем папку проекта в командной строке (КС). Вводим команду:

npm run watch


И нажимаем Enter

В папке src создадим файл spread.js и сразу укажим его в файле index.html

<script src="src/spread.js"></script>

Использование в массивах.

В файле мы создадим три массива.


let pet = ['dog', 'cat', 'hamster'];
let wild_animals = ['tiger', 'bear', 'wolf'];

let animals = ['whale', 'elephant'];



Первый массив - домашние животные, второй массив - дикие животные и третий массив - просто животные.

Теперь мы хотим добавить первые два массива в один общий массив - животные (animals).

Попробуем вариант - просто вставить переменные первых двух массивов в третий напрямую:


let pet = ['dog', 'cat', 'hamster'];
let wild_animals = ['tiger', 'bear', 'wolf'];

let animals = [pet, 'whale', wildanimals, 'elephant'];

console.log(animals);





Обратите внимание, что получилось.

Не то что мы хотели!

Получился в итоге многомерный массив, который содержит добавленные элементы в виде отдельных массивов.

Для получения желаемого результата, мы можем воспользоваться оператором разворота - spread operator - (...)

Все что нужно сделать - это поставить три точки перед переменными в массиве


let pet = ['dog', 'cat', 'hamster'];
let wild_animals = ['tiger', 'bear', 'wolf'];

let animals = [...pet, 'whale', ...wild_animals, 'elephant'];

console.log(animals);





Теперь массив animals содержит всех животных, и домашних, и диких.

Если посмотреть на транпилированный код в файле dist/spread.js


'use strict';

var pet = ['dog', 'cat', 'hamster'];
var wild_animals = ['tiger', 'bear', 'wolf'];

var animals = [].concat(pet, ['whale'], wild_animals, ['elephant']);

console.log(animals);



Как можно видеть, здесь используется метод.concat на пустом массиве[] и соединяем все массивы по порядку.

Это не самое элегантное решение!

Намного проще поставить три точки!

Использование в функциях.

Будем использовать оператор разворота для передачи аргументов в функцию. Создадим функцию, которая сложит три числа и выведет результат в консоле браузера.


function add(x, y, z) {
 console.log(x + y + z);
};

let num = [1, 2, 3];



И массив num, состоящий из трех элементов.

Функция add принимает три аргумента и явно будет работать неправильно, если мы передадим ей в качестве аргумента весь массив.

Если сделать просто так add(num);



Как и ожидалось.

Теперь давайте попробуем передать массив, как аргументы в функцию, при помощи оператора spread

add(...num);


function add(x, y, z) {
 console.log(x + y + z);
};

let num = [1, 2, 3];

add(...num);





Оператор разворота- это очень удобный инструмент для работы с массивами!

Его следует знать и правильно использовать для того, чтобы получить элегантный и легко читаемый код.



                                                                                                                                                             

четверг, 6 сентября 2018 г.

ES6. Постоянная const. Отличия от let и var(III)

Использование const





Все материалы по ES6


Помимо var и let мы можем использовать для объявления переменных ключевое слово - const.

const - constant - постоянная. Позволяет объявить постоянные значения, которые нельзя изменить в ходе программы.

Давайте попробуем применить const!

Вначеле запустим скрипт Babel, который будет следить за файлами и при их изменении создавать свежие версии файлов.

Для этого нам следует открыть папку проекта в КС (командной строке) и ввести команду:

npm run watch


и нажать Enter

Скрипт запустится и теперь Babel будет следить за файлами.

Генерированные файлы нас будут интересовать только в порядке ознакомления, так как современные версии браузеров поддерживают const без транспиляции.

В папке src я создал файл const.js.

В этом файле мы создадим постоянную PI использую const.

Выводим значение PI в консоле.

const PI = 3.14159;

console.log(PI);


Во мноих языках, включая JS, имена постоянных пишутся большими буквами, чтобы легко отличить их от переменных.

Тепреь важно изменить путь к файлу в файле index.html

<script src="src/const.js"></script>

Сохраняем и открываем в браузере.

И в консоле мы увидим значение PI.



Если мы посомтрим на то, что сгенерировал Babel, то мы увидим:

"use strict";

var PI = 3.14159;

console.log(PI);


Как вы видите. он просто заменил const на var.

Как я уже говорил, если бы мы даже использовали нетранспилированный файл с const, то никаких ошибок мы бы не увидили!

Постоянная может быть объявлена только один раз и значение ей должно быть присвоено сразу


Если этого не сделать, то произойдет ошибка.

Давайте посмотрим. Стоит только убрать значение у PI и произойдет ошибка



-Переменой не присвоено значение!-

Теперь, вернем значение и попробуем его изменить ниже.


const PI = 3.14159;
PI = 3.15;
console.log(PI);



Сохраним и посмотрим в консоле.

Теперь мы увидим другую ошибку



-Нельзя присвоить переменной иное значение. -

Постоянная, как и преременная объявленная через let не поднимаются.


То естсь нельзя использовать постоянную до её объявления!

Для примере перенесем вывод переменной до объявления константы:


console.log(PI);
const PI = 3.14159;



Мы получим ошибку.



- PI не объявлена -

Еще одна интересная особенность постоянных это то, что хотя мы и не можем поменять их значение, мы можем изменить значение свойства объекта, если он был объявлен при помощи const


С другой стороны в этом нет ничего странного, потому что если переменной присвоен объект, то в переменной будет находиться не сам объект, а ссылка на него.

Давайте посмотрим на примере.


const CAT = {
 COLOR: 'black'
};
console.log(CAT.COLOR);





Теперь мы изменим значение COLOR на white

const CAT = {
 COLOR: 'black'
};

CAT.COLOR = 'white';
console.log(CAT.COLOR);




И мы видим, что значение легко изменилось!

Нам это удалось. потому что мы изменили свойства объекта, а не саму постоянную!

При попытке изменить сам объект чаще всего происходит ошибка. В последних версиях - просто выдает настоящие значения - значения заданные при инициализации обьекта.

Например:


const CAT = {
 COLOR: 'black'
};

СAT = {
 COLOR: 'white'
};
console.log(CAT.COLOR);



Просто покажет в консоле black

Что использовать const, let или var ?

Если вы точно знаете, что значение переменной не изменится, тогда используйте - const.

В остальных случаях используйте let.

Есть мнение. что по умолчанию следует использовать const и только для значений, которые изменятся - let.

А что с var? К сожалению, при написании кода на ES6 в нем нет необходимости.



                                                                                                                                                             

вторник, 4 сентября 2018 г.

ES6. Переменная let. Области видимости и хостинг. (II)

Переменная let.





Сейчас пришло время более подробно познакомиться с переменной let и ее важнейшими отличиями от var.

Лучше всего это сделать на примерах.

В папке src в файле let.js запишем следующий код:


       if ( true ) {
         var temp = "ES5";
       }
       console.log(temp);



Запустим скрипт для транспиляции нашего кода. Если вы не помните настроек, то стоит посмотреть пост:ES6 подготовка к работе.

npm run watch


В папке dist появится файл let.jsсо следующим содержимым:

    "use strict";

    if (true) {
      var temp = "ES5";
    }
    console.log(temp);



Он ничем не отличается от исходного, кроме добавления "use strict".

Тпереь в файле index.html в теге script добавим путь к файлу let.js из папки dist.


     <script src="dist/let.js"></script>



Сохраняем и открываем файл в браузере. Откроем консоль и увидим:



Теперь, если мы поменяем var на let в папке src, то мы увидим следующее сообщение в консоле:



Это говорит о том. что наша переменная необъявлена!

Это произошло потому, что

переменная объявленная через let будет видна только внутри блока с фигурными скобками, в котором она объявлена!


В этом легко убедиться если посмотреть в папку dist/let.js:


    "use strict";

    if (true) {
     var _temp = "ES5";
    }
    console.log(temp);



У нас появилась переменная _temp, а в консоле выводится temp (без нижнего подчеркивания).

Хостинг - подьем переменных

Переменная объявленная с помощью let не поднимается и поэтому их нельзя использовать до их объявления!


Пример:

В файле index.html подключим исходный код из файла src/let.js.


    <script src="src/let.js"></script>



А в самом этом файле вызовем переменную до ее объявления:


     console.log(temp);
     let temp = "ES5";



Сохраним и посмотрим на консоль.



Там будет ошибка, потому что наша переменная не объявлена!

То есть мы не можем использовать переменную объявленную с помощью let до её объявления!

Если поменять let на var, то мы увидим:



Ошибки не будет. Будет undefined

Мы видим undefined потому, что перед тем как выполнить код, движок JS поднял переменную temp наверх и присвоил ей значение undefined, после чего мы вывели значение в консоль, и только после этого, присвоили переменной temp значение ES5

Это выглядит вот так:


    var temp, //undefined 
    console.log(temp);
    var temp = "ES5";



Рассмотрим применение let на практике.

Создадим пять кнопок в файле index.html

src/let.js Все удалим и объявим переменную buttons и поместим в нее все кнопки:

var buttons = document.querySelectorAll('button');

Далее простой цикл for внутри которого мы объявили переменную button и присваеваем ей все кнопки по порядку. А в качестве текcта кнопки будет значение i

button.innerText = i;

При нажатии на кнопку я хочу, чтобы в консоле отобразился её порядок:

  
     button.onclick = function(e) {
           console.log(i); 
      };

Файл let полностью:

    var buttons = document.querySelectorAll('button');

    for (var i = 0; i < buttons.length; i++) {
        var button = buttons[i];
        button.innerText = i;
        button.onclick = function(e) {
           console.log(i); 
        };
    }



Сохраняем. Открываем в браузере и видим, что при нажатии на любую кнопку отображается в консоле только цифра 5.



Если вы не сталкивались с этой проблемой. то сразу не понятно, что не так.

В C# или Java аналогичный код работал бы правильно.

Все дело в том, что когда мы пишем var i, то мы создаем глобальную переменную, которая будет видна всем функциям, которые мы создали для наших кнопок в качестве обратного вызова.

Это тоже самое. что записать наш код так:


    var buttons = document.querySelectorAll('button');
    var i;
    for ( i = 0; i < buttons.length; i++) {
        var button = buttons[i];
        button.innerText = i;
        button.onclick = function(e) {
           console.log(i); 
        };
    }



Теперь ВСЕ функции видят одну и тужe i.

Так работают замыкания в JavaScript!

Чтобы исправить ситуацию, нам нужно сделать так, чтобы на каждый цикл создавалась своя переменнная i. Тогда и каждая функция получит свою i!

Для этого мы используем let!

Меняем var на let и проблема решена!



Файл src/let.js


    var buttons = document.querySelectorAll('button');

    for (let i = 0; i < buttons.length; i++) {
        var button = buttons[i];
        button.innerText = i;
        button.onclick = function(e) {
           console.log(i); 
        };
    }



Из этого можно сделать вывод, что let делает работу с переменными более понятной и интуитивной. Особенно для людей с опытом работы в других языках.

                                                                                                                                                             

ES6 подготовка к работе.

Установка и настройка Babel.





Для работы с ES6 нам нужно установить Babel и сделать некоторые настройки.

Для этого нужно установить последнюю стабильную версию Node.js и NPM.

Если у вас они не установлены, то вы сможете сделать это с официальных сайтов, перейдя по ссылкам выше.

Установка очень простая и я на ней останавливаться не буду.

После установки Node.js вы сможете проверить версию набрав в КС (командная строка - здесь и далее)

node -v


npm -v
- для версии NPM.

Создадим рабочую папку из КС - в нужной директории

mkdir Имя_Папки_проекта


Для перехода в папку (директорию).

cd Имя_папки_(или_директории)


Или можно просто перетащить нужную папку прямо в КС и нажать Enter, таким образом получить путь и перейти к нужной папке.

Переходим в созаную папку.

Теперь нам нужно инициализировать NPM.

В КС набираем:

npm init -y


В нашем случае -y обозначает, что мы принимаем все параметры по умолчанию (Yes).

В папке вашего проекта после этого появится файл package.json В этом файле содержится информация необходимая для работы NPM.

Вернемся к Babel



Babel предназначен для траспиляции кода. Транспиляция - это перевод кода с одного языка на другой. В нашем случае с ES6 на ES5. После этого мы сможем использовать код в любом современном браузере.

Babel можно использовать с другими инструментами и фреймворками. На сайте Babel есть подробная инструкция.

Пройдя по этой ссылке Using Babel мы сможем с вами выбрать нужную нам конфигурацию. Так как у нас есть Node.js, NPM и мы собираемся использовать CLI , то наша команда сформируется так

npm install babel-cli babel-core babel-preset-es2015 --save-dev


cli - comande line interface Интерфейс командной строки - позволит использовать команду babel в самой КС, как NPM.

babel-core - основой модуль Babel.

babel-preset-es2015 - модуль еобходимый для транспиляции ES6.

--save-dev добавит модули в package.json в раздел devDependencies, в которм указываются модули необходимые для разработки.

После установки в папке проекта появится папка node_modules.

В этой папке содержатся папки модулей, которые мы устаовили. Также эти модули добавились в наш файл package.json в раздел devDependencies.

В папке проекта создадим папку src Source-Источник - для исходного кода. и папку dist Distribution -распространиение.- конечный код.

index.html - (так же в корне папки проекта) файл с помощью которого мы будем проверять работоспособность кода.

В этом файле обычная разметка с тегом script


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
 <script></script>
</body>
</html>



Проверить Babel

В папке src создадим файл script.js.

Внутри файла мы попробуем одно из нововедений ES6


    let variable = "Item";



let - ключевое слово, которе позволяет объявлять локальные переменные.

Теперь нам нужно перевести (транспилировать) ES6 в ES5.

Для этого в файле package.json в разделе scripts удаляем скрпт-тест ("test": "echo \"Error: no test specified\" && exit 1") вставим следующее -

"build": "babel src -d dist --presets es2015"

Это мы написали скрипт, который будет брасть исходный код из папки src. -d - указывает куда поместить измененный код - dist.

--presets es2015 - указывает что мы транспилируем код ES 6.

Сохраняем

В КС

npm run build


Этой командой мы можем запускать скрипты, которые мы указываем в файле package.json

Нажимаем Enter Запустится скрипт и когда он завершится в папке dist

Появится файл script.js вот с таким содержимым:


    "use strict";

    var variable = "Item";



Как вы видете слово let заменено на var и значит все транспилировалось и можно использовать код на странице.

Для того, чтобы каждый раз не вводить команду

npm run build


Мы добавим еще один скрипт в файл package.json

"watch": "babel src -d dist --presets es2015 -w"

-w означает, что теперь Babel будет смотрет за файлами в папке src. И как тоько будут изменения в файлах или вообще в папке src (например - создание нового файла) то тут же все автоматически отразится уже готовым кодом в папке dist.

package.json
{
  "name": "ES-6-new",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "babel src -d dist --presets es2015",
    "watch": "babel src -d dist --presets es2015 -w"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.3",
    "babel-preset-es2015": "^6.24.1"
  }
}



Здесь файл package.json полностью с выделенными красным цветом изменениями.

Теперь запускаем команду в КС:

npm run watch


И теперь запишем в наш файл script.js в папке src

let say = "Hello";

Сохраним.

И в файле script.js в папке dist отразятся наши изменения:

var say = "Hello";

Остановить слежение - Ctrl + C и Y если остановить или N - если продолжить.

См картинку ниже:



Таким простым способом мы настроили транспилцию кода в режиме реального времени, таким образом, чтобы любой браузер смог прочитать JS- код написанный по новым стандартам.

Теперь самое время перейти к более серьезному изучению JS стандарта ES6.                                                                                                                                                              

пятница, 1 июня 2018 г.

Скрыть / Показать содержимое поля ввода.

Довольно часто бывает необходимость добавить некий функционал полю ввода. Например - срыть / показать пароль пользователю, при вводе данных. Это значительно облегчает введение данных, особенно в случаях неисправности клавиатуры пользователя (западают кнопки, не пропечатываются, или не работает Caps или Shift.В общем, вещь не только привлекательная, но и порой, даже нужная!




В интернете вы найдете массу различных способов, как это сделать, но они, как правило, все написаны с библиотекой jQuery. Подключать библиотеку только для этого, бывает излишним, а переписывать нужные функции отдельно - трудозатратно, поэтому я решил написать код на чистом JavaScript.

Без долгих предисловий.

Рассмотрим вариант самого простого функционала - просто показать / скрыть пароль кликом на кнопку:

Вариант 1:самый простой.


Меняется только надпись на кнопке.
В файле HTML ничего особенного. Просто поле ввода пароля и кнопка. Им добавлены идентификаторы - id, для того, чтобы можно было с ними работать при помощи JavaScript.

JavaScript код я не стал выносить в отдельный файл и записал его здесь же.

Здесь мы получаем доступ к полю ввода и кнопке. Сохраняем их в глобальные переменные для того, чтобы каждый раз по клику не получать доступ - что является ресурсно затратной операцией.

На кнопку вешаем событие - click и запускаем функцию.

Внутри функции мы проверяем значение атрибута type у элемента input. Если это password, то мы меняем его на атрибут text, и наоборот.

Заодно и поменяли одержимое (название) кнопки button.

Можно это не делать, если не хотите добавлять достаточно затратную операцию .innerHTML. Для этого достаточно удалить код отмеченный красным.

Ниже полностью код файла index.html

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>

</head>
<body>
 <p>Enter the Password: <input type="password" id="show1"></p>
 <button id="show">Show password</button>

 <script>
  var input = document.getElementById("show1");
  var button = document.getElementById("show");
  button.onclick = show;

  function show () {
   if(input.getAttribute('type') == 'password') {
    input.removeAttribute('type');
    input.setAttribute('type', 'text');
    button.innerHTML='Hide password';

   } else {
    input.removeAttribute('type');
    input.setAttribute('type', 'password');
    button.innerHTML='Show password';

   }
  }
 </script> 
</body>
</html>


Вариант с иконкой.


Смена надписи на кнопке и иконки в любой части кода

Идем на сайт Font Awesome и выбираем понравившиеся иконки.

Подключаем Font Awesome через CDN в нашем хедере.

Добавляем одну иконку на страницу.

Например: <i id="i" class="far fa-eye"></i>

Теперь мы будем менять не только атрибут, но и класс элемента i, поэтому нам нужно добавить элементу идентификатор id

Остальное здесь все почти точно так же.

Ниже полностью код файла index.html


        <!DOCTYPE html>
 <html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
  <!-- Font - Awesome -->
   <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
 </head>
 <body>
  <!-- <i class="far fa-eye-slash"></i> вторая иконка закомментированна-->
  <p>Enter the Password: <input type="password" id="show1"></p>
  <button id="show">Show password</button><i id="i" class="far fa-eye"></i>
  
  <script>
   var button = document.getElementById("show");
   button.onclick = show;
   var input = document.getElementById("show1");
   var icon = document.getElementById("i");
   function show () {
    if(input.getAttribute('type') == 'password') {
     input.removeAttribute('type');
     input.setAttribute('type', 'text');
     button.innerHTML='Hide password';
     icon.className = 'far fa-eye-slash';
    } else {
     input.removeAttribute('type');
     input.setAttribute('type', 'password');
     button.innerHTML='Show password';
     icon.className = 'far fa-eye';
    }
  }
 </script>
 </body>
 </html>



Несомненный плюс этого метода, это то, что иконку можно разместить в любой части сайта. Все примеры, я намеренно привожу без CSS, потому как это добавит вам простор для различных способов стилизации и оформления документа.

В этом коде все будет работать так как показано на картинке, но стоит помнить, что класс мы меняем новым свойством браузера, которое не поддерживается браузером IE < 10 версии.



В таких случаях можно использовать jquery или вариант ниже.

Третий вариант



Смена надписи и иконки внутри кнопки

Этот вариант вообще простой и очень похож на первый, потому что мы здесь меняем только содержимое кнопки кодом .innerHTML, куда мы положили и наши иконки.

Ниже полностью код файла index.html


 <!DOCTYPE html>
 <html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
  <!-- Font - Awesome -->
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
  <!-- your file js -->
  <script src="main.js" defer></script>
 </head>
 <body>

  <p>Enter the Password: <input type="password" id="show1"></p>
  <button id="show">Show password  <i id="i" class="far fa-eye"></i></button>
  
  <script>
   var button = document.getElementById("show");
   button.onclick = show;
   var input = document.getElementById("show1");
   var icon = document.getElementById("i");
   function show () {
    if(input.getAttribute('type') == 'password') {
     input.removeAttribute('type');
     input.setAttribute('type', 'text');
     button.innerHTML='Hide password  <i id="i" class="far fa-eye-slash">';
    } else {
     input.removeAttribute('type');
     input.setAttribute('type', 'password');
     button.innerHTML='Show password  <i id="i" class="far fa-eye">';
    }
   }
  </script>
 </body>
 </html>





Вариант с подменой вводимых символов.

Приведу такой вариант просто для информации. Если честно, то совершенно не могу представить, где он может пригодиться.

Суть в том, что мы можем сохранить то что вводит пользователь так как ест, а вместо этого подставить в поле ввода случайные символы.

То есть. Пользователь вводит, например - vvvvvvvvv, а в поле ввода видит - ZfhEysUFKk.
Точно. как на картинке ниже.



Для этого мы сохраняем то что ввел пользователь в переменную str, а вместо этого в this.value выводим случайный символ, полученный путем генерации случайного числа (от 65 до 122 - потому как это цифровое значение символов в разных регистрах разрешенных для паролей).

Затем, мы из случайного числа генерируем символ - String.fromCharCode(); И уже этот символ выводим в поле ввода присвоив его переменной this.value Здесь приведу сам js скрипт


        var str =  "";  // то что ввел юзер
 document.getElementById("test").onkeypress = function (event) {
  // то что ввел юзер
  // var str = this.value;
  str = str + event.key;
  console.log(str);

  // вывод в инпут случайных символов
  this.value += String.fromCharCode(getRandomInt(65, 122));
  // запрет вывода в инпут того что вводит пользователь 
  return false;
 }
 // функция генерации случайного числа от - до
 function getRandomInt(min, max) {
   return Math.floor(Math.random() * (max - min)) + min;
 }



                                                                                                                                                             

воскресенье, 27 мая 2018 г.

Автосохранение файлов в Sublime3.

Очень часто при написании кода требуется быстро посмотреть изменения файла в браузере, а постоянное нажатие "Сохранить" или Ctrl + S утомляет, то на помощь приходит такая фишка, как автосохранение файлов при потере фокуса (переключение на др. вкладку) в Sublime.

Без долгих предисловий. Ничего скачивать и устанавливать не надо. Просто идете по пути -> "Preferences" -> "Settings".

См фото ниже:



В открывшемся окне редактора, в файле Preferences.sublime-settings-User (справа), добавляем следующую строку

"save_on_focus_lost": true,


Точно так как на фото ниже:



Файл этот, обычно находится по адресу:

C:\Users\UserName\AppData\Roaming\Sublime Text 3\Packages\User


Вот и все!

Теперь при переключении на браузер у вас всегда будут автоматически сохраняться все изменения в редакторе.

Хорошего кодинга! :-)

                                                                                                                                                             

понедельник, 14 мая 2018 г.

React.js (8) Отладка




Все статьи по React.js



Если вы точно воспроизвели данный код у себя на машине, то могли заметить, что смена статей при нажатии на кнопку происходит один раз в два клика. То есть нажимаем один раз - ничего не происходит. нажимаем второй раз - меняется порядок статей.

Давайте разберемся. что у нас пошло не так и заодно научимся отладке React приложений.

В каждом случае, когда у вас идет то-то не так, вам нужно просто вспомнить как работает React и повторить за ним эти шаги.

В нашем случае, все что у нас произойдет - в какой-то момент по клику (1) на кнопку вызовется collback (2) он поменяет состояние и это должно привести к перестроению виртуального DOM и это внесет изменения в реальный DOM.



Давайте проверим все ли работает у нас так, как задумано?

В первую очередь нас интересует вызывается ли этот collback(2)?

Нам для этого ненужны какие-то специальные средства отладки. просто добавим console.log('---',1); в эту функцию.

следующим этапом, если все пойдет правильно, то у нас вызовется setState() он в любом случае, приведет к перестроению виртуального DOM, и соответственно вызовется метод render().

В него тоже добавим console.log('---',2);

Третье, что может пойти не так - это, теоретически у нас может как-то не так поменяться состояние. В функции render() добавим в console.log('---',2, this.state);

Идем в консоль - Ctrl + Shift + I и видим:



Collback - вызывается, все вызывается и состояние, на самом деле, меняется. Вроде бы все хорошо. Почему же тогда не работает? Давайте проанализируем наш код:

<ArticleList articles={this.state.reverted ?articles.reverse() : articles}/>

В каждый момент времени он получает массив статей. Или перевернутый - ? articles.reverse() или обычный -

: articles

Проблема в том, что метод articles.reverse() не просто возвращает перевернутый массив, но и меняет этот объект по ссылке!. Таким образом, когда мы в следующий момент времени захотим просто передать в ArticleListмассив статей, он уже будет перевернутый.

Чтобы это продемонстрировать мы добавим в вывод консоли (2) articles.map(article => article.id

console.log("---",2, this.state, articles.map(article => article.id))

И для наглядности вынесем действие смены массива ArticleList наверх.


  import React, {Component} from 'react'
  import ArticleList from './ArticleList'
  import articles from '../fixtures.js'
  import 'bootstrap/dist/css/bootstrap.css'

  class App extends Component {
    state = {
      reverted: false
    }
    render(){
      const articlesList = this.state.reverted ?articles.reverse() : articles
      console.log("---",2, this.state, articles.map(article => article.id))
      return (
        <div className="container">
          <div className="jumbotron">
            <h1 className="display-3">
                App name
                <button className="btn" onClick = {this.revert}>Revert</button>
            </h1>
          </div>  
            <ArticleList articles={articlesList}/>
        </div>
      );
    }
      revert = () => {
        console.log("---",1)
       this.setState({
        reverted: !this.state.reverted
      })
    }
  }
  export default App



Давайте посмотрим



Массивы действительно меняются местами, но когда мы нажимаем еще раз, и у нас уже {reverted: false} (2) мы все еще ссылаемся на предыдущий массив с тем же порядком. Соответственно, пока мы его не перевернем в следующий раз. ничего не произойдет.

Попробуем "починить" это "наивным способом".

Добавили articles.reverse()



Таким образом у нас каждый раз будет по клику меняться направление. Даже наш флажок мы оставили без изменений - reverted: !this.state.reverted, потому как он уже ни на что влиять не будет.

В ArticleList мы просто передадим articles. Мы будем их менять по ссылке - <ArticleList articles={articles}/>

Проверим и убедимся, что это избавит нас от этой проблемы. Но на самом деле наша ошибка значительно глубже!

Обратите внимание на то, что мы делаем! Это ужасно!!! Никогда так не делайте!!!

На самом деле сейчас мы по ссылке меняем массив (на скрине сверху обвел красным), который может использоваться где угодно в нашем приложении. Мы же не знаем где еще мы считаем эти articles. Поэтому правило:

Работайте в компонентах с локальными переменными, которые вы видите где используются и как вы с ними взаимодействуете!


Иначе, может получиться так, что вы поменяете переменную в одном месте, а потом не сможете понять, почему соседний компонент работает не так, как вы это задумали! Поэтому->

Никогда не меняйте по ссылке внешние переменные и тем более то, что приходит вам в props


Теперь давайте попробуем исправить ситуацию.

Давайте, при создании компонента, мы создадим локальную копию этого массива.

articles = articles.slice()

И теперь у нас для каждого компонента App будет своя копия articles. И теперь с копией мы можем работать. Нам нужно только добавить this к изменению порядка в функции reverse()->

this.articles.reverse()

и

<ArticleList articles={this.articles}/>

Теперь мы не будем менять глобальную переменную articles, а меняем только то, что "живет" в this.

Уже лучше! И все казалось бы работает нормально, но здесь мы рискуем столкнуться с еще одной проблемой.

Давайте предположим, что мы решили сделать любой наш компонент - PureComponent, соответсвенно, чтобы у нас не происходило лишних перестроений виртуального дерева, когда это не нужно. И заменим Component на PureComponent

import React, {PureComponent} from 'react'

и соответственно -

class App extends PureComponent

И аналогично в файле index.js перепишем ArticleList перепишем с функционального компонента в классовый компонент. И тоже сделаем его PureCompomemt

index.js

  import React, {PureComponent} from 'react'
  import Article from '../Article'
  import './style.css'
  export default class ArticleList extends PureComponent {
    render(){
        const articleElements = this.props.articles.map((article, index) =>
            <li key = {article.id} className="article-list__li">
              <Article article = {article} defaultOpen = {index === 0}/>
            </li>
          )
        return(
            <ul>
              {articleElements}
            </ul>
          );
    }
  }



Посмотрим, как это работает. Идем в приложение. Нажимаем на кнопку Revert и ...ничего не происходит!

Почему так?

Давайте пройдемся по нашему алгоритмы.

В первую очередь - в какой момент времени у нас должно было что-то произойти?

В функции revert (App.js) произошло изменение состояния, значит в render, начнется перестроение виртуального дерева.

Ставим проверочный вывод в консоль в самом верху рендера - console.log("---",1)

Затем, в рамках перестроения общего виртуального дерева будет перестраиваться и ArticlaList (index.js) поставим там - console.log("---",2), так же в самом начале рендера. И теперь посомотрим, что происходит у нас в консоле.



В первый раз (1 на фото) - сработали обе функции, потому что DOM дерево строилось в первый раз.

Потом, мы нажали кнопку Revert и у нас сработала только 1 функция (2 на фото)

Если заменить PureComponent на обычный Component, то будут срабатывать обе функции.

Так происходит потому что с точки зрения ArticleList, когда мы будем проверять props в shouldComponentUpdate(), props у нас не поменяются.

Единственный props , который к нам приходит - this.props.articles

Но articles мы меняем по ссылке. Таким образом это всегда один и тот же массив.Соответственно, каждый раз при престроении мы передаем туда ссылку на один и тот же массив и несмотря на то, что внутренние составляющие его поменялись (порядок). Но с точки зрения ArticleList это все тот же массив. И он будет считать, что перестроение делать ненужно!

Это одни из главных причин почему в React настолько популярна работа с иммутабельными данными.

Иммутабельные - это данные, которые вы не меняете по ссылке

Если вам нужно, в какой-то период времени, поменять массив статей, то вы не просто меняете по ссылке какую-то локальную переменную, но вместо этого вы создаете новый массив. И уже этт новый массив будет содержать статьи в таком порядке, в котором вы и хотите.

Соответственно правильным будет вот такое решение!

App.js

  import React, {PureComponent} from 'react'
  import ArticleList from './ArticleList'
  import articles from '../fixtures.js'
  import 'bootstrap/dist/css/bootstrap.css'

  class App extends PureComponent {
    state = {
      reverted: false
    }
    render(){
      console.log('---',1)
      return (
        <div className="container">
          <div className="jumbotron">
            <h1 className="display-3">
                App name
                <button className="btn" onClick = {this.revert}>Revert</button>
            </h1>
          </div>  
            <ArticleList articles={this.state.reverted ? articles.slice().reverse() : articles}/>
        </div>
      );
    }
      revert = () => {
       this.setState({
        reverted: !this.state.reverted
      })
    }
  }
  export default App



Это, почти также как и было в самом начале, но единственное что - это для того, чтобы вы ничего не меняли по ссылке, чтобы articles оставался такой же как и ранее, мы создали копию (articles.slice()) и переворачивали уже копию. Не меняя при этом оригинальный массив.

Вот теперь у нас все будет хорошо!

Никогда не меняйте ничего из внешних переменных!

Никогда не меняйте ничего, что приходит в props!

Лучше, вообще ничего не менять по ссылке, и каждый раз, нужно какое-то изменение, создайте локальную копию и меняйте уже её!


Все статьи по React.js



Файлы, которые мы изменяли в этот раз:


App.js ( со всеми изменениями последний на странице)

components/ArticleList/index.js

  import React, {PureComponent} from 'react'
  import Article from '../Article'
  import './style.css'
  export default class ArticleList extends PureComponent {
    render(){
      console.log("---",2)
        const articleElements = this.props.articles.map((article, index) =>
            <li key = {article.id} className="article-list__li">
              <Article article = {article} defaultOpen = {index === 0}/>
            </li>
          )
        return(
            <ul>
              {articleElements}
            </ul>
          );
    }
  }



                                                                                                                                                             


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com