Webpack mantendo a qualidade do seu JavaScript

Até o momento vimos dois posts de introdução ao Webpack, nesse post iremos implementar linters em nosso código JavaScript, se você nunca ouviu falar em linting tools, elas são ferramentas de optimização de código, elas nos ajudam a escrever o código organizado e com boas práticas. Mas isso não significa que iremos prevenir bugs, mas a diferença que teremos um código fácil de debugar em em grandes times teremos uma estilo único de desenvolvimento.

Junto ao linters responsáveis pela validação básica podemos integra-los com guias de desenvolvimento, eles utilizam um conjunto padrões de desenvolvimento para realizar uma validação mais restrita, com isso tempos uma melhor manutenção do nosso código. No mercado front-end temos guias específicos para JavaScript e CSS, muitos guias de CSS estão atrelados a guias visuais, mas também podemos encontrar guias para automatização de validação CSS com Webpack e os principais Guias de estilo do mercado são:

Guias JavaScript

Guias CSS/LESS/SASS +HTML

  • Fractal – é uma biblioteca de components com style guide tempos alguns plugins de integração com webpack.
  • Airbnb CSS / SASS Style guide – contém regras de declaração, seletores, formatação, comentários, entre outras regras específicas para SASS
  • Indiomatic CSS Principles – contém regras gerais, whitespace, comentários, formatação e uma série de exemplos práticos.
  • Stylelint Config Standard – essa configuração extends da configuração recomendada e aplica um regra de uma série de guias

Por que utilizar Guia de padrão de desenvolvimento ?

Já abordarmos algumas vantagens mas vale a pena revisar:

  • Ajuda na prevenção de erros
  • Formatação automática em alguns casos
  • Padronização de desenvolvimento
  • Facilidade de manutenção de código
  • Fácil configuração, uma vez definido o estilo você poderá compartilhar a mesma configuração com o seu time
  • Incentivo a boas práticas de desenvolvimento

Ok, Já entendi agora como eu utilizo esses guias com Webpack?

Esse tutorial vamos continuar o trabalho dentro do repositório utilizado nos posts anteriores e implementar a validação do nosso código JavaScript, agora vamos criar uma nova branch chamada step-10 para realizarmos os primeiros passos. Inicialmente vamos instalar utilizando nosso terminal o eslint e eslint-loader em nosso projeto, eles serão os pacotes responsáveis pela validação do nosso código EcmaScript:

npm install eslint-loader eslint --save-dev

Após a instalação dos pacotes precisamos configurar o nosso eslint, podemos criar o nosso arquivo de configuração “.eslintrc” manualmente ou utilizar o terminal para instalação, para facilitar o nosso processo vamos utilizar o terminal, para isso executamos o seguinte comando em nosso terminal:

eslint --init

Quando executamos o comando anterior nosso terminal irá realizar uma série de perguntas que serão elas:

  • Como você gostaria de configurar ESLint?
    R. Usando um style guide popular
  • Qual style guide você gostaria de seguir
    R. Airbnb
  • Você usa React?
    R. No
  • Qual o formato que você quer o seu config file?
    R. JavaScript
  • Você gostaria de instalar as dependências necessárias?
    R. Y(sim)

Feito isso, o arquivo .eslintrc será criado com o seguinte código:

module.exports = {
    "extends": "google"
};

Agora que nos temos o eslint instalado e configurado vamos, inclui-lo em nosso webpack.config.js:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
          'eslint-loader',
        ],
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ],
  },
};

Este é o arquivo de configuração utilizado nos posts anteriores, incluímos o eslint-loader em nossas regras de leitura para arquivos JavaScript ele agora irá trabalhar em conjunto com o babel loader.

Rodando o webpack novamente se tudo ocorrer bem teremos o seguinte retorno:

Nosso lint irá começar a validar o nosso código

Lendo os nossos errors e warnings um ponto de atenção para o document. O eslint não reconhece document porque não especificamos qual ambiente estamos desenvolvendo, para isso vamos fazer uma alteração em nosso arquivo .eslintrc:

module.exports = {
    "extends": "airbnb-base",
    "env" : {
        "browser": true,
    }
};

Com essa nova propriedade “env” definimos browser como true, agora o eslint passará considerar que nosso será escrito para browsers. Podemos também especificar que nosso código irá rodar em servidores node.

Rodando nosso código novamente:

Ainda temos alguns erros e warnings podemos resolver de duas maneiras manualmente ou como um autofix, para realizar a correção automática você pode realizar via terminal ou através de configurações no webpack.config.js adicionando opções ao nosso eslint-loader da seguinte forma:

{
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
          {
            loader: 'eslint-loader',
            options: {
              fix: true,
            },
          },
        ],
      },
Depois de rodar webpack com autofix temos os seguintes alertas relacionados ao nosso console.log

Podemos remover o console.log do nosso código essa é uma ação recomendada, imagina você enviar um código em produção cheio de console log, no Webpack podemos definir qual será o modo que iremos trabalhar “development” ou “production” partindo desse princípio podemos. Iremos criar uma condicional em nosso webpack.config quando executarmos o nosso código para produção iremos disparar alertas para remover os console.log de nosso código.

Para essa etapa iremos trabalhar na branch step-11, primeira mudança vamos incluir dois scripts em nosso package.json:

{
  "name": "webpack-tutorial",
  "version": "1.0.0",
  "description": "Webpack tutorial",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev" : "webpack --watch --mode=development",
    "build" : "webpack --mode=production"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fellyph/webpack-tutorial.git"
  },
  "keywords": [
    "tutorial",
    "webpack"
  ],
  "author": "fellyph",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/fellyph/webpack-tutorial/issues"
  },
  "homepage": "https://github.com/fellyph/webpack-tutorial#readme",
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "babel-loader": "^8.0.5",
    "css-loader": "^2.1.0",
    "eslint": "^5.15.1",
    "eslint-config-airbnb-base": "^13.1.0",
    "eslint-config-google": "^0.12.0",
    "eslint-loader": "^2.1.2",
    "eslint-plugin-import": "^2.16.0",
    "file-loader": "^3.0.1",
    "node-sass": "^4.11.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1"
  }
}

Agora temos dois novos scripts dev e build, eles incluem o modo que iremos exportar modo de produção irá criar um código minificado sem source map já o código gerado para desenvolvimento terá recursos que irão facilitar o debug de nosso código.

Quando quisermos exportar a versão de desenvolvimento executamos em nosso terminal o comando:

npm run dev

Ele irá executar o comando “webpack –watch –mode=development” assim executaremos o webpack no modo desenvolvimento ainda inclui mais um parâmetro “–watch” para o webpack ficar assistindo todas as mudanças dentro do projeto assim só precisamos rodar o nosso código uma vez. Para exportar nosso script de produção rodamos o comando:

npm run build

Agora com esses dois novos recursos vamos fazer as seguinte alteração em nosso webpack.config:

const path = require('path');

module.exports = (env, options) => ({
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
          {
            loader: 'eslint-loader',
            options: {
              fix: true,
              rules: {
                'no-console': (options.mode === 'development') ? 'off' : 'warn',
              },
            },
          },
        ],
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ],
  },
});

Temos várias maneiras de verificar se estamos criando código para produção ou desenvolvimento, no caso acima converti module.exports de um objeto para uma função para resgatar o mode dentro das opções definidas em nosso código executado no terminal.

Dentro das opções eslint-loader adicionamos uma condicional para saber se vamos desligar o alerta para o console.log, este é um exemplo para uma regra especifica mas podemos colocar qualquer regra dentro dessa condicional todas as regras você pode conferir nesse link

Introdução Webpack Parte 2: Loaders

No post anterior vimos uma introdução ao Webpack, criamos uma situação básica carregando múltiplos arquivos JavaScript e vimos que Webpack possui 4 elementos chaves: Entry(arquivo de entrada), output(arquivo de saída), loaders e plugins. Nesse tutorial vamos abordar como trabalhamos como loaders, Webpack em seu core nada mais é que JavaScript carregando JavaScript. Nada mais básico que em uma aplicação web termos uma série de assets com diferentes extensões, jpg, css, js, svg… e como o Webpack se trata com essa situações, a resposta é: Loaders

Loaders

Loaders dão Webpack a possibilidade de processar diferentes tipos de arquivos, define como o Webpack irá ler e exportar os assets do seu projeto. Escritos em Node.JS os loaders precisam regras escritas em expressão regular para definir quais extensões serão carregadas pelo tal.

Temos vários loaders e podemos separa-los nas seguintes categorias:

  • Arquivos
  • JSON
  • Transpiling
  • Templating
  • Styling
  • Linting
  • Frameworks

Para nosso tutorial vamos utilizar os seguintes recursos, babel para fazer o transpiling do nosso ECMAScript2015+ e JSX e Sass para fazer o pre-processamento do nosso estilo. Com esse panorama vamos precisar dos seguintes loaders:

  • Arquivos: url-loader, file-loader
  • Transpiling: babel-loader
  • Styling: style-loader, css-loader, sass-loader

Com essa lista vamos seguir um passo-a-passo e falar sobre cada um desses loaders durante a evolução de nosso projeto.

babel-loader

Hoje desenvolvimento com JavaScript moderno é um requisito quase que obrigatório em projetos web, em nossos exemplos anteriores fizemos o uso de EcmaScript 5, versão suportada por todos os browsers do mercado incluindo internet Explorer, mas se a gente quiser fazer o uso de EcmaScript2015+ e não deixar de fora os usuários de browsers mais antigos, reposta será “transpiling”, ele funciona como uma compilação de código, onde temos EcmaScript 2015+ sendo convertido para EcmaScript 5, no mercado temos algumas ferramentas que realizam esse processo atualmente a ferramenta mais usada no mercado é babel.

Webpack é umas da tecnologias compatíveis com babel, assim podemos fazer o processo de transpiling utilizando babel para criar o nosso bundle. Para isso vamos primeiramente instalar babel-loader em nosso projeto, dentro do nosso terminal, na pasta do seu projeto executamos o seguinte comando:

npm install -D babel-loader @babel/core @babel/preset-env

Nesse código temos a instalação do babel-loader, babel core e preset-env, presets são coleções de plugins para o babel suportar uma certa versão, ainda temos presets para cada versão do EcmaScript, react e typescript. Mas a preset-env tem o foco no target environment, ou seja, no ambiente final. Ele suporta as versões mas recentes do ecmaScript, mas o código gerado conseguimos determinar qual será o nível de suporte. Após a instalação dos pacotes nosso package.json ficará da seguinte forma:

{
  "name": "webpack-tutorial",
  "version": "1.0.0",
  "description": "Webpack tutorial",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fellyph/webpack-tutorial.git"
  },
  "keywords": [
    "tutorial",
    "webpack"
  ],
  "author": "fellyph",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/fellyph/webpack-tutorial/issues"
  },
  "homepage": "https://github.com/fellyph/webpack-tutorial#readme",
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "babel-loader": "^8.0.5",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1"
  },
  "dependencies": {
    "npm": "^6.7.0"
  }
}

Depois de instalado os pacotes necessários, vamos incluir o loader em nosso webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};

No código acima adicionamos um novo atributo chamado module, ele será responsável por definir as regras de como os nosso módulos serão carregados, passamos um array de regras(rules) contendo os seguintes parâmetros:

  • test: contém uma expressão regular para achar os arquivos que iremos aplicar a regra, na sua maioria utilizamos a extensão que buscamos.
  • exclude: também uma expressão regular, contendo os arquivos ou pastas que iremos ignorar.
  • use: qual o loader iremos utilizar para esse caso.

Por exemplo, em nosso código definimos que todos arquivos que terminam com a extensão .js iremos usar o loader ‘babel-loader‘, dentro do nosso código de entrada adicionamos EcmaScript2015 se conferimos o código gerado temos, por exemplo, a keyword ‘const’ convertida para ‘var’. O exemplo completo você pode conferir na branch step-6.

style-loader e css-loader

Continuando a evolução de nosso webpack.config.js vamos carregar o nosso estilo, para essa tarefa vamos instalar dois loaders css-loader e style-loader, css-loader vai ser responsável por carregar o css junto com webpack e style-loader vai pegar o conteúdo carregado pelo webpack e exportá-lo em uma tag style. Agora em nosso terminal iremos rodar o seguinte código:

npm i -D style-loader css-loader

Completa a instalação vamos adicionar o loader em nosso webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        exclude: /(node_modules|bower_components)/,
        use: ['style-loader', 'css-loader'],
      }
    ]
  }
};

Após adicionarmos a configuração para carregar nosso css o padrão será o mesmo, regex para achar os arquivos css, quais o loaders iremos utilizar com a propriedade use, adicionei um css simples chamado main.css:

:root {
  --c-text:#28262C;
  --c-component-background: #F9F5FF;
  --c-primary: #14248A;
  --c-secondary: #D4C2FC;
  --c-ternary: #998FC7;
  --e-padding: 1em;
}

.navigation,
.main,
.header,
.footer {
  padding: var(--e-padding);
}

.navigation {
  background: var(--c-primary);
}

.main {
  background: var(--c-component-background);
}

.header {
  background: var(--c-component-background);
}

.footer {
  background: var(--c-ternary);
}

Em nosso index.js vamos carregar o css, isso mesmo, faremos o import dentro do nosso arquivo de entrada:

import './css/main.css';

import './js/content';
import './js/header';
import './js/footer';

Quando executarmos o comando webpack em nossa linha de comando teremos a seguinte saída, o arquivo gerado o main.js e os arquivos que serviram de entrada para nosso bundle. O código para esse loader você irá encontrar na branch step-7

Vamos adicionar uma image ao nosso projeto via css, a imagem abaixo será a imagem que iremos trabalhar voce pode salva-la e adicionar na pasta assets/imgs/

Com a image no local definido vamos fazer a seguinte alteracao em nosso CSS:

:root {
  --c-text:#28262C;
  --c-component-background: #F9F5FF;
  --c-primary: #14248A;
  --c-secondary: #D4C2FC;
  --c-ternary: #998FC7;
  --e-padding: 1em;
  --header-img: url(../../assets/imgs/webpack-logo.png);
}

.navigation,
.main,
.header,
.footer {
  padding: var(--e-padding);
}

.navigation {
  background: var(--c-primary);
}

.main {
  background: var(--c-component-background);
}

.header {
  background: var(--header-img);
  background-repeat: no-repeat;
}

.footer {
  background: var(--c-ternary);
}

Vamos adicionar a nossa imagem como background do nosso header e tentar compilar o nosso código:

Temos um erro de compilacao do nosso codigo

Isso acontece porque não temos o loader apropriado para a extensão .png, para esse caso podemos trabalhar com loaders:

  • file-loader: irá carregar o arquivo e criar uma copia do arquivo em nossa pasta de output(dist)
  • url-loader: irá carregar o arquivo especificado e irá criar uma copia no formato base64.

Para corrigir o nosso projeto iremos instalar dos dois loaders:

npm i -D file-loader url-loader

Com os loaders instalados vamos combinar os dois loaders em uma solução, arquivos pequenos convertemos em base64 e arquivos pesado criamos uma copia em nossa pasta dist, como podemos ver em nosso webpack.config.js (step-8):

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        exclude: /(node_modules|bower_components)/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ]
  }
};

Agora temos loaders para nossos arquivos .js .css e images, caso queiramos trabalhar com pre-processadores, por exemplo, SASS ou LESS, parra o nosso tutorial iremos trabalhar com SASS mas o processo é similar para o LESS. Primeiro em nosso terminal iremos instalar os pacotes necessários:

npm install sass-loader node-sass --save-dev

Com tudo pronto vamos alterar a regra de leitura no nosso css para também aceitar arquivos SASS.

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(svg|gif|png|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 100,
          fallback: 'file-loader',
          publicPath: '/img',
          outputPath: '/img',
        },
      },
    ]
  }
};

A principal diferença em nosso webpack.config.js é regex que anteriomente procurava arquivos com a extensão .css agora com a regra ‘(sa|sc|c)ss’ procura arquivos .sass .scss ou .css e por fim adicionamento o sass-loader, se trocarmos a extensão do nosso arquivo .css por .scss ele irá carregar normalmente. Exemplo:

_variables.scss

$c-text:#28262C;
$c-component-background: #F9F5FF;
$c-primary: #14248A;
$c-secondary: #D4C2FC;
$c-ternary: #998FC7;
$e-padding: 1em;
$header-img: url(../../assets/imgs/webpack-logo.png);

main.scss

@import 'variables';

.navigation,
.main,
.header,
.footer {
  padding: $e-padding;
}

.navigation {
  background: $c-primary;
}

.main {
  background: $c-component-background;
}

.header {
  background: $header-img;
  background-repeat: no-repeat;
}

.footer {
  background: $c-ternary;
}

O código completo desse tutorial você irá encontrar na branch step-9 E até o próximo tutorial.

Work smart with Gutenberg

Hi folks, last weekend I’ve done my first talk in 2019, at WordCamp Prague it was a pleasure to be again in Prague, one of my favourite city in Europe and my presentation it was about Gutenberg, I have created a small project with a few examples using static blocks, editable and dynamic blocks, follow the slides:

The repository with the examples from my presentation you can find here: https://github.com/fellyph/digital-agency-block-kit

Introdução a Webpack

Webpack é um static module bundler ou numa tradução genérica um “empacotador de módulos”, mas o que isso significa? Webpack vai gerenciar a leitura de arquivos de seu projeto empacotando esse conteúdo da forma que você desejar. Ele irá funcionar de forma recursiva, um arquivo funciona como ponto de partida, a partir desse ponto inicial ele irá varrer todos os arquivos que estão conectados em sua aplicação e para cada caso teremos um tratamento específico.

Qual a vantagem disso tudo? Ainda é comum aplicações web carregando dezenas JavaScript com diferentes script tags, se você não fizer isso da maneira correta irá causa uma série de problemas de performance em sua aplicação. E o gerenciamento de módulos ataca alguns problemas de performance, por padrão Webpack trabalha apenas com o carregamento de Javascript, mas podemos instalar recursos necessários para trabalhar com CSS, imagens, arquivos SASS, LESS e TypeScript. Esses “recursos” são chamados de loaders que veremos em breve.

Atualmente na versão 4 Webpack esté entre as principais ferramentas de configuração de ambiente, anteriormente ocupadas pelo Gulp e o Grunt. O que impulsionou sua popularidade foi a adoção por grandes CLI’s do mercado, com angular-cli e create-react-app conseguimos abstrair toda a complexidade de realizar a configuração inicial de projetos com Angular ou React e Webpack é um grande responsável por toda essa mágica por trás dos CLIs.

Vale salientar que funcionamento do Webpack é um pouco diferente do Gulp e Grunt, eles são gerenciadores de tarefas enquanto o Webpack é um gerenciador de módulos, o seu foco é como os arquivos serão carregados separados por extensão, mas claro que podemos trabalhar uma série de recursos que o gulp/grunt realizam, por exemplo, toda parte de optimização, minificação, tratamento de arquivos e concatenação.

O processo de leitura com Webpack é dividido em 4 conceitos chaves:

  • Entry: qual será o nosso arquivo de entrada, o nosso ponto inicial
  • Output: nosso arquivo final gerado
  • Loaders: para cada tipo de arquivo o Webpack possui um loader devemos gerenciar este ponto no nosso arquivo de configuração
  • Plugins: Webpack disponibiliza uma série de plugins, para diferentes tipos de situações

Nas versões anteriores nos iniciávamos um projeto com o arquivo webpack.config.js nele especificamos como o Webpack ira se comportar. Na versão nova do Webpack podemos iniciar um projeto sem o arquivo de configuração, mas voce quiser realizar optimizações extra com o o webpack.config.js

Preparativos

Para esse tutorial vamos criar um projeto simples com dois arquivos, a estrutura será a seguinte, cada passo desse tutorial irei criar uma branch para voce acompanha a evolução dos arquivos, por exemplo, step-1… step-2… step-3… O nosso ponto de partida será o seguinte:

Temos um aquivo index.html e dentro da pasta SRC temos uma index.js

Antes de iniciar a instalação do Webpack, voce irá precisar de uma versão atualizada do nodeJS/NPM, podemos verificar a versão do nosso NPM com o comando em nosso terminal:

npm -v

Na raiz no nosso projeto vamos inicializar o nosso pacote NPM(step-2), nele vamos salvar todas as dependências para o nosso projeto, assim em nosso terminal executamos o comando:

npm init

Na imagem acima temos o final do passo-a-passo realizar pelo NPM init, ele ira verificar se ja temos um arquivo package.json, caso não, ele irar nos ajudar a criar um, fazendo perguntas sobre os campos necessários e ao final ele gerar uma arquivo package.json:

{
  "name": "webpack-tutorial",
  "version": "1.0.0",
  "description": "Tutorial about webpack",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fellyph/webpack-tutorial.git"
  },
  "keywords": [
    "tutorial",
    "webpack"
  ],
  "author": "fellyph",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/fellyph/webpack-tutorial/issues"
  },
  "homepage": "https://github.com/fellyph/webpack-tutorial#readme"
}

Com o nosso package.json configurado agora vamos instalar o webpack e webpack-cli em nosso projeto(step-3), voltando para o terminal vamos executar os seguintes comandos:

npm install webpack webpack-cli --save-dev

Se tudo correr bem teremos a confirmação em nosso terminal:

Caso a versão do seu node não suporte a última versão do Webpack, voce receberá um alerta para atualizar o seu nodeJS and NPM. Agora vamos criar a determinada situação para fazer uma comparação após usarmos Webpack. Inicialmente vamos adicionar três novos scripts(content.js, header.js e footer.js) ao nosso projeto e carrega-los em nossa index.html(isso voce poderá acompanhar na branch step 4).

Simulando uma conexão lenta o resultado será o seguinte. Temos 4 requisições durando um total de 4.29 segundos.

Uma ação de melhorar a performance de um site é reduzir o numero de requisições, em browsers modernos e conexões HTTP2 isso não tao visível. Mas quando não temos esse recursos temos uma limitação de requisições em paralelo isso causa um efeito cascata aumentando o tempo de carregamento. Uma solução para isso será enviar um único bundle contendo todos os arquivos, agora vamos verificar como fazer isso com webpack primeiro vamos definir o nosso webpack.config.js:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  }
};

No arquivo acima tempos temos instruções qual é o arquivo de entrada neste caso o index.js e qual arquivo será gerado dentro da pasta ‘dist’ o arquivo main.js. E nosso index.js importa os três arquivos, content.js, header.js e footer.js

var myContent = require('./js/content');
var myHeader = require('./js/header');
var myFooter = require('./js/footer');

O Webpack irá ler o arquivo index.js e varrer todos os arquivos relacionados e criar um arquivo único simulando uma conexão lenta o resultado será o seguinte:

Agora temos duas requisições 2 requisições em 4.24 segundos

Até o momento não fizemos nenhuma otimização esse post serviu como uma introdução, nos próximos posts podemos evoluir o nosso projeto e aumentar sua complexidade. O código final você encontra aqui: https://github.com/fellyph/webpack-tutorial/tree/step-5

Internacionalização com blocos Gutenberg

Se você está acessando o blog pela primeira vez esse post faz parte de uma série de posts relacionados na criação de blocos para o novo editor do WordPress Gutenberg. Caso queira conferir os posts anteriores:

Internacionalização dentro da construção de temas é o fator fundamental, nele damos a possibilidade do tema se adaptar a diversos idiomas. Essa regra também se aplica aos nossos blocos, caso queiramos criar um produto que atenda diversos idiomas, precisaremos permitir que toda saída de texto do nosso bloco se a adapte ao idioma do WordPress.

Antes do Gutenberg a internacionalização de texto era feita no back-end através da função wp_localize_script() . Agora com o Gutenberg é possível fazer a internacionalização no front-end, para isso vamos trabalhar como uma biblioteca javascript chamada wp-i18n. Se você já trabalhou com a internacionalização de temas o processo será o mesmo, precisamos especificar uma domínio para nossas String, nada mais é que um id que é adicionado no cabeçalho do nosso tema ou plugin. Após definido nosso domínio (text domain) fazemos a chama de texto da seguinte forma:

__( 'Hello, dear user!', 'my-text-domain' );

Essa função que envolve o texto será responsável por conferir se o tema ou plugin possui tradução para essa string, que será armazenado em um arquivo .pot. Ele pode ser gerado automaticamente por algumas ferramentas. No caso de blocos Gutenberg podemos utilizar o WordPress i18n babel plugin para gerar nosso arquivo .pot.

Para nosso tutorial vamos utilizar os blocos criados nos exercícios anteriores e o resultado será o seguinte:

/**
 * 1 - Criando primeiro bloco Gutenberg com ES5: parte 04
 * 	-	 internacionalizacao de blocos
 */
( function( blocks, element, editor, i18n ) {
	var el = element.createElement,
			RichText = editor.RichText;

	//registrando nosso bloco
	blocks.registerBlockType( 'fellyph/tutorial-03', {
		title: i18n.__('Bloco com cadastro de atributo', 'fellyph'),
		icon: 'smiley',
		category: 'layout',
		keywords: [
			i18n.__('Tutorial', 'fellyph'),
			i18n.__('Cadastro', 'fellyph'),
			i18n.__('Dinâmico', 'fellyph')
		],
		
		//registrando o tipo de bloco que iremos trabalhar
		attributes: {
			content: {
				type: 'string',
				source: 'html',
				selector: 'p',
			}
		},
		
		// função responsável por exibir nosso bloco no editor
		edit: function(props) {
			var content = props.attributes.content;

			// função responsável por acompanhar as mudanças do nosso atributo
			function onChangeContent( newContent ) {
				props.setAttributes( { content: newContent } );
			}
			
			// agora retornamos um elemento RichText ao invés de um parâgrafo
			return el(
				RichText,
				{
					tagName: 'p',
					className: props.className,
					onChange: onChangeContent,
					placeholder: i18n.__('Insira texto aqui', 'fellyph'),
					value: content,
				}
			);
		},

		save: function(props) {
			return el(
				RichText.Content , {
					tagName: 'p',
					className: props.className,
					value: props.attributes.content,
				}
			);
		},
	});
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.editor,
	window.wp.i18n
) );

Na primeira linha adicionamos uma nova dependência o script i18n, dentro do nosso script trocamos todas as chamadas de strings que serão exibidas para o usuário, no front-end e no editor de nossa aplicação pela função i18n.__() , nela vamos passar o texto original e o text domain para tradução como parâmetros.

Para aumentar a quantidade de texto eu adicionei uma nova propriedade em nosso bloco keywords, ela ajudar na busca de nossos componentes, no caso adicionamos 3 keywords em um array: Tutorial, Cadastro e Dinâmico com isso quando realizamos uma consulta as keywords podem ser um critério para achar o nosso bloco como podemos ver na imagem abaixo:

A keyword ajuda a achar o nosso bloco

O resultado do nosso bloco será praticamente o mesmo, com uma diferença que o nosso texto agora está localizado:

Podemos ver nos detalhes do nosso bloco que estamos carregando o título que cadastramos e no bloco podemos ver o nosso placeholder.

O último passo será criar o nosso arquivo de tradução o .poc, temos duas opções, a primeira seria gerando com software especifico o poedit, caso você queira evitar tooling com Babel e Webpack:

A seguida opção com o pacote nodeJS @wordpress/babel-plugin-makepot, para isso precisamos figuração de nosso projeto com babel e Webpack isso vai demandar um post específico dai entramos na criação de blocos com JSX.

O código completo você irá encontrar em meu github: https://github.com/fellyph/Gutenberg-tutorials/tree/tutoriais/gutenberg-04

Até o próximo post!

Gutenberg: Trabalhando com atributos em blocos customizados

Esse post é o terceiro de uma série de posts sobre o blocos Gutenberg customizados. Nesse tutorial vamos abordar como trabalhar com atributos em posts customizados. Nos posts anteriores vimos como registrar o nosso bloco customizado e adicionar um estilo ao nosso markup. Agora vamos entender como os atributos funcionam dentro do WordPress. Caso ainda não viu os posts anteriores você confere aqui:

Atributos

Até o momento, nos posts anteriores, quando registramos o nosso primeiro bloco passamos apenas um paragrafo estático, mas caso queiramos dar a possibilidade para o usuário cadastrar uma informação dinamicamente através do editor como serão os próximos passos? Vimos durante a chamada da função edit podemos receber um objeto com propriedades relacionadas ao nosso bloco e uma dessas informações é a propriedade attributes. Para trabalhar com a edição de conteúdo vamos trabalhar com alguns componentes do WordPress.

Componentes

Os blocos muitas vezes repetem a mesma complexidade, isso nos permite aplicar a reutilização de certos recursos, para simplificar a edição de conteúdo, o core do WordPress tem uma série componentes React que realizam tarefas específicas, mas não se assuste você não precisa aprender React para utilizar esse recursos. Mas meu conselho se você que trabalhar de forma mais produtiva é uma tecnologia que ajuda bastante, mas o foco desse tutorial vamos trabalhar com EcmaScript5, Dentro dos componentes vamos abordar dois componentes:

RichText

Ele irá renderizar para nos um input editável com a possibilidade de inclusa de texto e links. Dentro do componente RichText temos as seguintes propriedades:

  • value: espera um valor do tipo string, será o conteúdo armazenado ate o momento, podendo ser texto ou markup valido.
  • onChange: esse atributo espera uma função, será o callback responsável quando o valor do atributo sofre uma alteração
  • placeholder: O valor do campo quando estiver vazio. isso é válido tanto para o input text ou textarea o valor será o mesmo
  • multiline: Se o bloco irá permitir que o usuário cadastre multiplas linhas quando presionar enter, quando desabilitado o cada enter irá criar um novo bloco.

BlockControls

Quando nosso bloco é renderizado dentro do editor, uma barra de ferramentas é exibida com algumas opções relacionadas ao bloco selecionado, por exemplo, caso queiramos exibir opções de alinhamento de texto ao nosso bloco passamos o elemento responsável por alinhamento. O BlockControls espera o seguinte:

  • icon: slug do dashicon para ser mostrado na barra de controle
  • title: um título será exibido em forma de tooltip para o usuário
  • subscript: Um texto opcional para ser exibido adjacente ao ícone da barra de controle
  • isActive: um booleano para definir se o barra de controle será selecionada ou não por default.

Para esse tutorial vamos registrar dois blocos, primeiro exemplo(script.js) apenas com um richText component e o segundo bloco fazendo o uso do blockControl e richText(script-controls.js).

script.js

/**
 * 1 - Criando primeiro bloco Gutenberg com ES5: parte 03
 * 	-	 carregando atributos
 */
( function( blocks, element, editor ) {
	var el = element.createElement,
	RichText = editor.RichText;

	//registrando nosso bloco
	blocks.registerBlockType( 'fellyph/tutorial-03', {
		title: 'Primeiro bloco Gutenberg: Parte 03',
		icon: 'smiley',
		category: 'layout',
		
		//registrando o tipo de bloco que iremos trabalhar
		attributes: {
			content: {
				type: 'string',
				source: 'html',
				selector: 'p',
			}
		},
		
		// função responsável por exibir nosso bloco no editor
		edit: function(props) {
			var content = props.attributes.content;

			// função responsável por acompanhar as mudanças do nosso atributo
			function onChangeContent( newContent ) {
					props.setAttributes( { content: newContent } );
			}
			
			// agora retornamos um elemento RichText ao invés de um parâgrafo
			return el(
				RichText,
				{
					tagName: 'p',
					className: props.className,
					onChange: onChangeContent,
					value: content,
				}
			);
		},

		save: function(props) {
			return el(
				RichText.Content , {
					tagName: 'p',
					className: props.className,
					value: props.attributes.content,
				}
			);
		},
	});
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.editor
) );

Como podemos ver no código acima, vamos ter mais uma dependência a classe editor do WordPress presente no script wp-editor, nela vamos encontrar os componentes relacionados a edição de conteúdo. Próximo passo adicionamos em uma variável nosso RichText, seguindo nosso código dentro do registerBlockType temos algumas alterações, adicionamos uma nova propriedade attributes ele é responsável pela configuração do nosso atributo. Ela merece um post específico só falando sobre

Mas voltando para o nosso tutorial definimos a configuração do atributo content esse nome pode ser qualquer um, por exemplo, “conteúdo”. Para esse atributo passamos um objeto de configuração como o tipo do dado(type), a fonte que iremos receber do editor(source), valores padrões(default) entre outras informações.

Dentro da função do edit, o nosso elemento RichText definimos os atributos tagName, className, onChange, value. Também criamos uma função onChangeContent ela é necessária por conta de um conceito do React de controle de estado, precisamos implementar um controlled component para isso, um resumo bem grosseiro, precisamos conectar o valor do input com o estado do component.

Index.php

Algumas diferenças na implementação do index.php, são o registro de dois blocos e quando registramos nosso script precisar adicionar a dependência ao ‘wp-editor’.

<?php

/**
 * Plugin Name: Gutenberg tutorial
 * Plugin URI: https://github.com/fellyph/gutenberg-tutorials
 * Description: Este tutorial ensina como criar um bloco gutenberg https://blog.fellyph.com.br/wordpress-2/criando-seu-proprio-bloco-gutenberg/.
 * Version: 1.3
 * Author: fellyph
 */

defined( 'ABSPATH' ) || exit;

/**
 * 1 - Criando nosso primeiro bloco: Parte 03
 *    1.1 - Adicionando o recurso de cadastrar atributos
 * 		1.2 - Adicionando o bloco com block control
 */

function meu_primeiro_bloco_gutenberg_parte_03 () {

	if ( ! function_exists( 'register_block_type' ) ) {
		// Checamos se temos suporte a função register_block_type antes de tudo.
		return;
	}

	wp_register_script(
		'tutorial-03',
		plugins_url( 'script.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element', 'wp-editor' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'script.js' )
	);

	wp_register_script(
		'tutorial-03-alinhamento',
		plugins_url( 'script-controls.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element', 'wp-editor' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'script-controls.js' )
	);

	wp_register_style(
		'style-editor',
		plugins_url( 'editor-style.css', __FILE__ ),
		array('wp-edit-blocks'),
		filemtime( plugin_dir_path( __FILE__ ) . 'editor-style.css' )
	);

	wp_register_style(
		'style-frontend',
		plugins_url( 'style.css', __FILE__ ),
		filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
	);

	register_block_type( 'fellyph/tutorial-03', array(
		'style' => 'style-frontend',
		'editor_script' => 'tutorial-03',
		'editor_style' => 'style-editor'
	));

	register_block_type( 'fellyph/tutorial-03-alinhamento', array(
		'style' => 'style-frontend',
		'editor_script' => 'tutorial-03-alinhamento',
		'editor_style' => 'style-editor'
	));
}

add_action( 'init', 'meu_primeiro_bloco_gutenberg_parte_03' );

Um ponto importante em nosso script, quando registramos um novo id(fellyph/tutorial-03-alinhamento e fellyph/tutorial-03) para nosso bloco precisamos atualizar o nosso seletores CSS:

.wp-block-fellyph-tutorial-03-alinhamento,
.wp-block-fellyph-tutorial-03 {
  background: #12c2e9;
  border: 3px solid #666;
  box-shadow: 0 0 0 #666;
}

.wp-block-fellyph-tutorial-03-alinhamento:hover,
.wp-block-fellyph-tutorial-03:hover {
  transform: rotate3d(0, 0, 0, 0);
}

script-controls.js

/**
 * 1 - Criando primeiro bloco Gutenberg com ES5: parte 03
 * 	-	 carregando atributos com BlockControls
 */
( function( blocks, element, editor ) {
	var el = element.createElement,
	RichText = editor.RichText,
	AlignmentToolbar = editor.AlignmentToolbar,
	BlockControls = editor.BlockControls;

	//registrando nosso bloco
	blocks.registerBlockType( 'fellyph/tutorial-03-alinhamento', {
		title: 'Bloco com controle de alinhamento',
		icon: 'welcome-view-site',
		category: 'layout',
		
		//registrando os atributos que iremos trabalhar
		attributes: {
			content: {
				type: 'string',
				source: 'html',
				selector: 'p'
			},
			alinhamento: {
				type: 'string',
				default: 'none'
			},
		},
		
		// unção responsável por exibir nosso bloco no editor
		edit: function(props) {
			var content = props.attributes.content;
			var alinhamento = props.attributes.alinhamento;

		// função responsável por acompanhar as mudanças do nosso atributo
		function onChangeContent( newContent ) {
					props.setAttributes( { content: newContent } );
			}

			function onChangeAlignment (nextAlign ) {
				props.setAttributes( { alinhamento: nextAlign } );
			}

		// agora retornamos um array por que estamos utilizando dois componentes
		return [
			el( BlockControls, 
				{ key: 'controls' },
				el( AlignmentToolbar, {
							value: alinhamento,
							onChange: onChangeAlignment
					})
        ),
				el(
					RichText,
					{
						key: 'richtext',
						tagName: 'p',
						style: { textAlign: alinhamento },
						className: props.className,
						onChange: onChangeContent,
						value: content,
						placeholder: 'Insira seu nome aqui'
					}
			),
			];
		},

		save: function(props) {

			return el(
				RichText.Content , {
					tagName: 'p',
					className: props.className,
					style: { textAlign: props.attributes.alinhamento },
					value: props.attributes.content,
				}
			);
		},
	});
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.editor
) );

No código acima temos alguns pontos importantes diferente do primeiro código, em atributos adicionamos um novo valor, “alinhamento” ele será do tipo string e terá um valor padrão ‘none’.

Dentro da função edit adicionamos uma função para acompanhar as mudanças desse componente onChangeAlignment onde atualizamos o valor do atributo referente ao alinhamento.

Agora quando retornamos o nosso bloco utilizamos um array para passar mais de um componente, por que precisamos de um blockControls para passar as configurações necessárias e um RichText para o usuário cadastrar um texto.

Dentro do nosso blockControls podemos também passar uma série de outros componentes relacionados a configurações mas para esse caso vamos utilizar apenas o AlignmentToolbar :

Em seguida conectamos o valor do aligmentToolbar ao nosso RichText, tanto no preview na função edit quando no nosso front-end na função save, definindo o atributo style: { textAlign: alinhamento }.

E esse será o resultado dos dois blocos que criamos aqui:

Exemplo de RichText
bloco utilizando placeholder
Exemplo com BlockControls e AligmentToolbar

O código desse tutorial você irá encontrar no meu git, para cada tutorial estou criando uma branch:

https://github.com/fellyph/Gutenberg-tutorials/tree/tutoriais/gutenberg-03

Qualquer feedback deixe aqui seu comentário e até o próximo tutorial

Referências:

Criando seu próprio bloco Gutenberg: Parte 2

No post anterior vimos como trabalhar com blocos customizados, conceitos básicos de como registrar nosso bloco como um plugin e criar um markup básico com JavaScript. Nesse post vamos evoluir o nosso bloco todo código está disponível no Github. Primeiro passo vamos ver como os blocos trabalham com estilo externo.

Carregando estilo externo

Primeiro passo será criar nossos arquivos CSS, um arquivo para o front-end de nossa aplicação e o segundo para nosso editor.

style.css(front-end e editor):

.wp-block-fellyph-tutorial-02 {
  background: linear-gradient(45deg, #12c2e9, #c471ed, #f64f59);
  padding: 2em;
  color: white;
  font-size: 20px;
  box-sizing: border-box;
  transform: rotate3d(0, 0, 0, 0);
  transition: all 200ms ease-out;
  box-shadow: 3px 3px 3px #666;
  text-align: center;
}

.wp-block-fellyph-tutorial-02:hover {
  transform: rotate3d(1, 1, 1, -4deg);
}

editor-style.css(somente editor)

.wp-block-fellyph-tutorial-02 {
  background: #12c2e9;
  border: 3px solid #666;
  box-shadow: 0 0 0 #666;
}

.wp-block-fellyph-tutorial-02:hover {
  transform: rotate3d(0, 0, 0, 0);
}

Temos acima os dois códigos que iremos carregar em nossa aplicação, dois pontos importantes, um o nome da classe ela sempre irá iniciar com ‘wp-block-‘ mais o nome que registramos o nosso bloco, nesse caso o ‘fellyph/tutorial-02‘ irá se transformar em fellyph-tutorial-02, assim temos a classe wp-block-fellyph-tutorial-02.

Segundo ponto, quando trabalhamos com a função register_block_type ela espera 4 parâmetros:

  • style
  • script
  • editor_style
  • editor_script

As propriedades script e style representam os arquivos que serão carregados no editor e front-end da aplicação, enquanto editor_style e editor_script serão carregados apenas no editor do WordPress. Nesse caso o estilo no arquivo editor-style irá sobrescrever o estilo do arquivo style.css. Para ficar mais claro vamos ver o print do resultado desses dois estilos:

Estilo aplicado em nosso front-end
Estilo aplicado em nosso editor

Temos um spoiler de como ficará esse nosso bloco, mas como podemos observar, o bloco no editor herda todas as propriedades do bloco do front-end, por exemplo, alinhamento, padding e a cor da fonte, essas características foram de no arquivo style.css.

index.php

Agora vamos dar uma olhada em nosso arquivo principal:

<?php

/**
 * Plugin Name: Gutenberg tutorial
 * Plugin URI: https://github.com/fellyph/Gutenberg-tutorials
 * Description: Este tutorial ensina como criar um bloco gutenberg https://blog.fellyph.com.br/wordpress-2/criando-seu-proprio-bloco-gutenberg/.
 * Version: 1.2
 * Author: fellyph
 */

defined( 'ABSPATH' ) || exit;

/**
 * 1 - Criando nosso primeiro bloco: Parte 02
 *    - Carregando arquivo externo para nosso estilo
 */
function meu_primeiro_bloco_gutenberg_parte_02 () {

	if ( ! function_exists( 'register_block_type' ) ) {
		// Checamos se temos suporte a função register_block_type antes de tudo.
		return;
	}

	wp_register_script(
		'tutorial-02',
		plugins_url( 'script.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'script.js' )
	);

	wp_register_style(
		'style-editor',
		plugins_url( 'editor-style.css', __FILE__ ),
		array('wp-edit-blocks'),
		filemtime( plugin_dir_path( __FILE__ ) . 'editor-style.css' )
	);

	wp_register_style(
		'style-frontend',
		plugins_url( 'style.css', __FILE__ ),
		filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
	);

	register_block_type( 'fellyph/tutorial-02', array(
		'style' => 'style-frontend',
		'editor_script' => 'tutorial-02',
		'editor_style' => 'style-editor'
	) );
}

add_action( 'init', 'meu_primeiro_bloco_gutenberg_parte_02' );

Não vamos abordar todas as partes desse código caso não tenha visto a primeira parte dessa séria aqui está o link: https://blog.fellyph.com.br/wordpress-2/criando-seu-proprio-bloco-gutenberg.

Comparado ao tutorial anterior, acrescentamos duas funções wp_register_style, uma para estilo geral e outra para o estilo do nosso editor e por fim passamos como parâmetro na função register_block_type.

script.js

Em nosso script agora temos o seguinte código:

( function( blocks, element ) {
	var el = element.createElement;


	blocks.registerBlockType( 'fellyph/tutorial-02', {
		title: 'Primeiro bloco Gutenberg: Parte 02',
		icon: 'smiley',
    category: 'layout',
  
		edit: function(props) {
			return el(
				'p',
				{ className: props.className },
				'Este conteúdo será exibido no editor.'
			);
		},
		save: function(props) {
			return el(
				'p',
				{ className: props.className },
				'Este conteúdo será exibido para o usuário final.'
			);
		},
	} );
}(
	window.wp.blocks,
	window.wp.element
) );

Comparado a versão anterior passamos nas funções dentro das propriedades edit e save uma propriedade chamada props, ela contém informações referente ao nosso bloco e uma delas é o nome dessa classe, não obrigatório passar o className dinamicamente poderíamos passar um valor estático, mas para facilitar a manutenção e evitar conflitos com outros blocos utilizamos o nome de classe dinamicamente.

Uma observação sobre a propriedade “icon” caso queira saber os ícones disponíveis dentro do WordPress, só acessar o link: https://developer.wordpress.org/resource/dashicons/#smiley lá você irá encontrar uma série de ícones separados por categoria.

Na página de dashicons você pode conferir o id para cada ícone

No meu caso eu selecionei o ícone smiley, como podemos ver na imagem abaixo, quando clico no ícone temos seu id e como utiliza-lo em nosso CSS ou HTML, mas eu só irei precisar do id ‘dashincons-smiley’ em nosso script usamos ‘dashicons-‘ apenas smiley.

A nível de curiosidade eu alterei a função edit para ver quais são os valores passados dentro de props(você não precisa implementar isso).

edit: function(props) {
	var test = 'Este conteúdo será exibido no editor. / ';
	for(var key in props) {
		test += key + " : " + props[key] + ' / ';
	}

	return el(
		'p',
		{ className: props.className },
		test
		);
	},

O resultado foi o seguinte:

Dentro do objeto props temos as seguintes propriedades e funções:

  • name
  • isSelected
  • attributes
  • setAttributes (function)
  • insertBlocksAfter (function)
  • onReplace (function)
  • mergeBlocks (function)
  • clientId
  • isSelectionEnabled
  • toggleSelection (function)
  • className

Para a estilização de nosso bloco poderíamos trabalhar com atributos dinâmicos, mas esse será assunto para um próximo post. Caso queira saber mais sobre como trabalhar com estilo em blocos customizados só deixar um comentário.

O código desse tutorial você irá encontrar na branch(tutorais/gutenberg-02) do repositório: https://github.com/fellyph/Gutenberg-tutorials/tree/tutoriais/gutenberg-02