• Как масштабировать сайт под маленькие устройства?

    @ned4ded
    Верстка, Фронтенд
    Это решит проблему:
    <meta name="viewport" content="width=device-width">


    TL;DR

    "Портативные устройства часто рендерят страницы в виртуальном окне, называемым вьюпортом, ширина которого обычно больше ширины экрана" - mdn. Для обхода этого поведения используется свойство width мета-аттрибута viewport, равное значению device-width: content="width=device-width". В таком случае ширина вьюпорта будет равна ширине экрана устройства.

    width - не единственное свойство, позволяющие изменять заложенное поведение вьюпорта, полный список доступен на mdn или в спеке. Стоит отметить, что это драфт, но современные браузеры его стараются имплементировать.

    Другое свойство, используемое тобой в примере, - initial-scale, устанавливающие зум вьюпорта в определенное значение при первичном запуске сайта. Значение может варьироваться от 0.1 до 10. При 1 зум равен ширине вьюпорта (можно сказать, что в данном случае зум отсутствует). По дефолту же зум равен значению auto, который высчитывается путем деления исходного значения вьюпорта на значение реально отрендеренного холста, что позволяет отобразить весь холст на экране (формула чуть сложнее). В твоем случае зум равен ширине экрана (т.к. ширина вьюпорта равна ширине экрана через width=device-width), т.е. 320px. Когда ты устанавливаешь ограничение на минимальную ширину тела документа, равную, например, 480px, то по дефолту у тебя будет горизонтальный скролл при зуме равном 1. Это логичное поведение, заложенное в спеке. Это как пытаться посмотреть сайт с ограничение в 1600px в ширину с ноутбука, там тоже будет горизонтальный скролл.

    Чтобы обойти это поведение достаточно установить initial-scale в значение auto, или убрать это свойство вообще, т.к. это значение по дефолту.
    Ответ написан
    Комментировать
  • Как подключить сторонний polyfill (classlist-polyfill) в Babel?

    @ned4ded
    Верстка, Фронтенд
    Во первых, classlist-polyfill - не бабель-плагин, сл-но его нельзя добавить в бабель.

    Во вторых, classlist polifill - это полифил для dom браузера, а не для js (разница в том, что dom - это апи, с которым можно взаимодействовать по средствам запуска js в браузерном окружении). Бабель полифилит js, но не dom.

    В третьих, чтобы dom-полифил заработал в браузере, его нужно в этом браузере запустить, для этого его нужно импортировать перед тем скриптом, который будет этим полифилом пользоваться. Бабель - это транспайлер, он не занимается конкатенацией js-модулей в один файл, для этого нужно использовать вебпак, роллап или еще какой-либо бандлер.

    Подведем итог, тебе нужно сначала настроить вебпак, который будет собирать модули js в один файл; потом импортировать в script.js полифил для браузера; потом нужно прогнать script.js через бабель, чтобы получить заполифиленный js (этим занимается, например, вебпак, он собирает скрипты в один или несколько файлов, прогоняет файлы через транспайлер).
    Ответ написан
    4 комментария
  • Когда передается функция как аргумент мы получаем на нее ссылку или копию самой функции?

    @ned4ded
    Верстка, Фронтенд
    ссылку,

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

    частично на книге SICP был основан это курс, можешь попробовать обратиться к ней, в исходниках к этой книге используется функциональный язык Scheme (Structure and Interpretation of Computer Programs - Abelson, Sussman).

    не уверен, что конвейер и композиция имеют прямое отношение к фп.
    Ответ написан
    1 комментарий
  • Чем отличаются примитивные типы от литеральных?

    @ned4ded
    Верстка, Фронтенд
    Если типу присвоить значение, то оно станет литеральным типом.

    type CarNames = 'bmw' | 'mercedes';
    
    const cars: CarNames[] = ['bmw', 'mercedes', 'zhiguli'];
    //                                            ^^^^^^^ 
    //              TypeError: Type '"zhiguli"' is not assignable to type 'CarNames'.


    подробнее и с примерами в официальной документации.

    Соответственно, отвечая на первый вопрос: литеральный тип - это конкретное значение одного из примитивных типов. Хотя в хэндбуке говорится о литеральных типах только для строк и чисел, можно назвать "литеральным" и значения null, undefined, true, false, но в этом нет особо смысла.
    Ответ написан
    5 комментариев
  • Что я делаю не так с модульными стилями?

    @ned4ded
    Верстка, Фронтенд
    Селектор .col.s12 имеет большую специфичность, чем css-класс из модуля. Чтобы исправить отделите структуру грида от структуры компонента:
    import React, {Component} from 'react'
    import styles from './Videos.module.css'
    
    export default class Videos extends Component{
        render(){
            return(
                <div id="test1" className={`row ${styles.VideoRoom}`}>
                    <div className="col s12 center grey lighten-3">
                       <div className={styles.HostVideo}>
                          Видео хоста
                       </div>
                    </div>
                </div>
            );
        }
    }


    Либо если хотите именно переопределять стили колонки, то попробуйте воспользоваться директивой :global(.class) в css-модулях (подробнее). Должно получиться что-то вроде :global(.col).HostVideo.

    О специфичности можно почитать: тут, тут.
    Ответ написан
    1 комментарий
  • Можно ли сделать таск в галпе для создание начальной структуры?

    @ned4ded
    Верстка, Фронтенд
    Можно, но проще будет создать базовый репозиторий и использовать его в качестве основы для проектов.
    Ответ написан
  • Как складывается отрицание (!) и пустой массив?

    @ned4ded
    Верстка, Фронтенд
    Очень спорный вопрос для собеседования, откровенно говоря.

    Операторы сложения и вычитания имеют унарную версию: употребление такого оператора перед значением конвертирует значение в числовое.
    console.log(+"1"); // prints 1
    console.log(+"s"); // prints NaN


    В случае +[], что эквивалентно Number([]), массив преобразуется в 0. Соответственно в операции !+[] используется 2 оператора подряд (например, как !! во второй части выражения), сначала происходит преобразование массива в ноль с помощью унарного оператора сложения +[] и конвертация нуля в true с помощью оператора !. В первой части выражения получаем true.

    Массив рассматривается как truthy значение, сл-но двойной оператор not во второй части выражения дает значение true (грубо говоря, это способ конвертировать массив в булевый тип).

    В итоге (!(+[])) + (!![]) (я позволил себе расставить скобки, чтобы был более очевиден порядок выполнения), конвертится в true + true, что конвертится в 1 + 1, собственно, вот и ваша двойка.

    ps Я хз, люди, которые составляют такие задачи, видимо, просто глумятся.

    более наглядная последовательность преобразований:
    !+[] + !![];
     
    /* +[] converts to 0 */
    
    !0 + !![];
    
    /* !0 converts to true */
    
    true + !![];
    
    /* ![] converts to false */
    
    true + !false;
    
    /* !false coverts to true */
    
    true + true;
    
    /* both true values convert to numbers */
    
    1 + 1;
    
    /* equals 2 */


    Небольшой апдейт. На немой вопрос "почему конвертация массива в число дает 0, а конвертация массива в булевой тип дает true, тогда как true != 0?" можно ответить след. образом:
    а) Почему конвертация массива в число дает 0? Массив является объектом в js, для конвертации объекта в число его нужно сначала конвертировать в примитив. В данном случае для простого объекта вызывается метод toString(). Метод toString() на массиве возвращает конкатенацию строковых значений массива, что для пустого массива дает пустую строку "". Подробнее о логике конвертации объекта в примитив в спеке, ecma-262/5.1/9.1. Далее конвертируется строка в число, что для пустой строки равно 0, подробнее пункт 9.3.1 спеки.

    б) Почему конвертация массива в булевый тип дает true? Простой ответ: согласно пункту 9.2 спецификации. Если поразмышлять, то скорее всего это означает, что объект существует (а он не может не существовать, если существует ссылка на него).
    Ответ написан
    3 комментария
  • Как иметь постоянно актуальные данные?

    @ned4ded
    Верстка, Фронтенд
    Для этого используются websocket'ы и паттерн message broker для бека. Например, реализованный с помощью либы rabbitmq для php.

    Фронт подписывается на обновления через сокеты, бек отправляет уведомления о новых объектах, при получении сообщения фронт обновляет свои данные.

    10000к плоских объектов - это не много, но если там сложная вложенная структура с большое количеством полей и вы проводите какие-то операции затратные над объектами, то лагать будет, если вы не будете обрабатывать их в фоне.
    Ответ написан
    1 комментарий
  • Почему Typescript показывает ошибку?

    @ned4ded
    Верстка, Фронтенд
    Typescript показывает ошибку, потому что он типизирует любые ключи объекта, переданные в Object.keys и Object.entries, как строки (а не строковые константы). Это заложенное поведение. Причину называют разрабы следующую: "объект может иметь больше свойств во время рантайма, поэтому тип string[]" (тред1, тред2).

    Доступ к свойству объекта без ошибки в данном случае можно получить только если задать типизацию самостоятельно.

    Самое простое решение:
    const data: {
        hello: {
            wtf: string;
        };
    } = {
        hello: {
            wtf: 'world',
        },
    };
    
    for (const [key] of Object.entries(data) as [keyof typeof data, any][]) {
        console.log(data[key]); 
    }


    Читабельность такого кода, конечно, сильно страдает. Более подходящий вариант:
    interface Data {
        hello: {
            wtf: string;
        };
    }
    
    type DataKey = keyof Data;
    
    const data: Data = {
        hello: {
            wtf: 'world',
        },
    };
    
    for (const [key] of Object.entries(data) as [DataKey, any][]) {
        console.log(data[key]);
    }
    Ответ написан
    Комментировать
  • Как обратиться к контексту, у которого нет имени?

    @ned4ded
    Верстка, Фронтенд
    Нет, нельзя.

    В useContext() нужно передавать ссылку на объект контекста, которую вы не сохранили. В js к необъявленным объектам невозможно получить доступ. Более того, как только ссылка на объект не используется, сам объект удаляется garbage collector'ом.

    const MyContext = React.createContext();
    
    const {
        Provider: CompaniesStoreServiceProvider,
        Consumer: CompaniesStoreServiceConsumer
    } = MyContext;
    
    useContext(MyContext);


    Как вариант, вы можете сделать хелпер/декортатор, который будет создавать контекст и возвращать объект, содержащий ссылку на контекст, провайдер и консьюмер; деструктурировать уже его.
    Ответ написан
    Комментировать
  • Как ускорить выполнение JavaScript в браузере? Как распаралелить JavaScript?

    @ned4ded
    Верстка, Фронтенд
    Распараллелить - нет, но вы можете чанковать текст и парсить его чанки асинхронно, что освободит основной поток.

    const arr = [1, 2, 3, 4, 5];
    
    const rec = (theArr) => {
        const [first, ...rest] = theArr;
    
        if (!first) return;
    
        setTimeout(() => {
            /* do some parsing, example: */
            if ([3, 4].includes(first)) alert('found it!');
    
            return rec(rest);
        }, 0);
    };
    
    rec(arr);
    Ответ написан
    8 комментариев
  • Best practice бэкенда на express?

    @ned4ded
    Верстка, Фронтенд
    я просто оставлю это здесь
    Ответ написан
    Комментировать
  • Как собрать шаблон под React + Typescript?

    @ned4ded
    Верстка, Фронтенд
    У вас не установлен лоадер для typescript.

    Есть 2 варианта, 1ый - ts-loader, 2ой - babel-loader c typescript пресетом.

    Я предпочитаю второй, но в таком случае напрямую бабель не будет производить проверку типов и нужно использовать плагин fork-ts-checker. Проверка типов будет происходить параллельно с процессом компиляции вебпака.

    Вот пример для бабель-лоадера.

    // webpack.config 
    
    module.exports = {
        module: {
            rules: [
                {
                    oneOf: [
                        {
                            test: /\.(js|jsx|ts|tsx)$/,
                            exclude: /(node_modules|bower_components)/,
                            loader: 'babel-loader',
                            options: {
                                cacheDirectory: true,
                            },
                        },
    
                        {
                            use: 'file-loader',
                            exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
                        },
    
                        // ** STOP ** Are you adding a new loader?
                        // Make sure to add the new loader(s) before the "file" loader.
                    ],
                },
            ],
        },
    
        plugins: [
            new ForkTsCheckerWebpackPlugin({
                async: true,
                useTypescriptIncrementalApi: true,
                checkSyntacticErrors: true,
                eslint: true,
            }),
        ],
    };
    
    // babelrc
    
    {
        "presets": [
            "@babel/preset-env",
            "@babel/preset-typescript"
        ],
    }


    Не забудьте установить пресеты, лоадеры и плагин.

    PS, можете подсмотреть конфиг для create-react-app, там можно выудить много интересного.
    Ответ написан
    4 комментария
  • Как передавать свойства между одностраничными компонентами?

    @ned4ded
    Верстка, Фронтенд
    Вам нужен либо стор (Mobx, Redux), либо родительский компонент, внутри которого будет происходит взаимодействие двух дочерних

    console.log(Message.state.value);

    Здесь вы обращаетесь к статическому свойству класса Message, конечно это не будет работать
    Ответ написан
  • Как правильно подключить ExtractTextPlugin?

    @ned4ded
    Верстка, Фронтенд
    1. Вы забыли добавить инстанс самого плагина в массив plugins

    plugins: [
        new HtmlWebpackPlugin({ template: 'src/index.html'}),
        new ExtractTextPlugin("styles.css")
      ]


    В самом репозитории плагин помечен как deprecated, т.е. устаревший и неподдерживаемый. Воспользуйтесь, если есть такая возможность, плагином MiniCssExtractPlugin

    2. "Подскажите как её решить и чтобы результирующий css импортировался в simple-piskel-clone/src/index.js ?"

    Это невозможно, вебпак при импортировании стилей просто строит граф зависимостей и через HtmlWebpackPlugin добавляет линк на него в html файл. Т.е. вам нужно импортировать в js файл сам базовый .scss файл, тогда вебпак при бандле его распарсит по заданным условиям, скомпилирует в style.css и подгрузит его на страницу. Ну или если вы просто делаете сборку без HtmlWebpackPlugin, то вручную импортируйте собранный css в запрашиваемую клиентом страницу.
    Ответ написан
  • Windows 10 как рабочая среда для full-stack разработки?

    @ned4ded
    Верстка, Фронтенд
    "Или же лучше Вин10 + WSL и не парить мозг?" так и делаю, доволен

    Раньше работал на убунта в качестве гостя на виртуалке и вин10 в качестве хоста - памяти съедало как фальстаф на завтрак.

    Более того, у wsl отличная интеграция с vscode, который, на мой взгляд, лучший бесплатный редактор для фронтенд разработки (слез с атома год назад и вздохнул спокойно).
    Ответ написан
    3 комментария
  • Почему выбивает ошибку в функции?

    @ned4ded
    Верстка, Фронтенд
    Добрый день.

    Метод getItem возвращает либо Products, либо undefined, если продукт не найден. Сделайте терминальное условие и укажите строгую типизация для используемого массива далее.

    addToCart = (id: number) => {
      let tempProduct: Products[] = [...this.state.products];
      const item = this.getItem(id);
      if(item === undefined) {
        // ... инструкция при возникновении терминального условия, например, ошибка
        return;
      }
      const index = tempProduct.indexOf(item as Products); // строгая типизация для item
            
      // ...
    };


    Возможно, если вы укажите тип возвращаемого значения, то не потребуется терминальное условие (но это нужно проверить, я так навскидку не могу утверждать со 100% уверенностью). Такой подход подвержен багам при исполнении js.

    getItem = (id: number): Products => {
      return this.state.products.find(item => item.id === id);
    };


    BTW, я бы не стал называть на вашем месте тип Products во мн. ч. Просто Product, т.к. по сути это единичная сущность.
    Ответ написан
    5 комментариев
  • В чем отличие папок app от dist?

    @ned4ded
    Верстка, Фронтенд
    В названии, * ба-дам-тсс * (:

    Обычно в папках app / src / client и т.д. хранятся исходные файлы веб-приложения, в папках dist / build / www хранятся скомпилированные файлы.

    Но название не имеет особого значения, это скорее какое-то негласное соглашение о наименование, в любом проекте их можно (но не нужно) назвать по своему усмотрению.
    Ответ написан
    Комментировать
  • Компилятор для typescript в вебпаке для реакт проекта, объясните разницу?

    @ned4ded
    Верстка, Фронтенд
    Добрый день.

    Отвечая на ваш вопрос - "Почему в проектах для react частенько используется ts-loader, а не babel-loader?" - они взаимозаменяемы, т.к. оба являются транспайлерами, отличаются только входные данные.

    Отвечая на - "Не проще ли, чтобы все вместе разбирал babel?" - нет, не проще, бабель не производит проверку типов и все равно потребуется установка tsc и, соответственно, настройку вебпака для использования tsc для проверки типов перед сборкой кода через бабель. Несмотря на некоторые соприкасающиеся фичи, typescript является скорее другим языком программирования, тогда как babel берет за основу современные версии ecmascripta. Иными словами, js до компиляции бабелем можно запускать в современных или будущих браузерах, тогда как typescript никогда нельзя будет запускать напрямую в js среде без компиляции. И отсюда возникает вопрос, зачем использовать babel для компиляции синтаксиса typescript'a в js без проверки типов и иных фич tsc, когда у typescript'а есть свой транспайлер? На этот вопрос я предлагаю ответить вам.

    Вот ссылка на реализованные фичи в разных средах.
    Ответ написан
    Комментировать
  • Будет ли хорошей практикой такая структура приложения на Vue?

    @ned4ded
    Верстка, Фронтенд
    Добрый день.

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

    Сам по себе vue не общается с сервером и кейс его использования связан исключительно с отображением. О чем, собственно, и написано в их официальном гайде:

    The core library is focused on the view layer only


    Одна из концепций, которую можно назвать бизнес-логикой в отношении vue (и любого другого спа) - это, наверное, стейт приложения. Вы не можете отделить стейт от вью, потому что на нем базируется реактивное отображение данных, без чего, собственно, у вас будет не спа, а прокачанный шаблонизатор, работающий на фронте. Даже представить себе реализацию такого не могу.

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

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