📜 ⬆️ ⬇️

React, Web Components, Angular and jQuery are friends forever. Generic JavaScript Components

image
This article is about how to write a generic JavaScript component that can be used.



Why and who needs it


The world of JavaScript development is very fragmented. There are dozens of popular frameworks, most of which are completely incompatible with each other. In such conditions, the developers of JavaScript components and libraries, choosing one specific framework, automatically reject a very large audience, which this framework does not use. This is a serious problem, and the article suggested its solution.


How everything will be implemented


  1. Let's write a React component.
  2. Using the preact and preact-compat JavaScript libraries, which work in the same way as React and weigh a measly 20 kilobytes, we will write wrappers for everything else.
  3. Set up the assembly using a Webpack.

Writing component code


For example, let's develop a Donut Chart of this type:


Donut chart


There is nothing surprising, we will not see - just the code.


import React from 'react'; export default class DonutChart extends React.Component { render() { const { radius, holeSize, text, value, total, backgroundColor, valueColor } = this.props; const r = radius * (1 - (1 - holeSize)/2); const width = radius * (1 - holeSize); const circumference = 2 * Math.PI * r; const strokeDasharray = ((value * circumference) / total) + ' ' + circumference; const transform = 'rotate(-90 ' + radius + ',' + radius + ')'; const fontSize = r * holeSize * 0.6; return ( <div style = {{ textAlign: 'center', fontFamily: 'sans-serif' }}> <svg width = {radius * 2 + 'px'} height = {radius * 2 + 'px'}> <circle r = {r + 'px'} cx = {radius + 'px'} cy = {radius + 'px'} transform = {transform} fill = 'none' stroke = {backgroundColor} strokeWidth = {width} /> <circle r = {r + 'px'} cx = {radius + 'px'} cy = {radius + 'px'} transform = {transform} fill = 'none' stroke = {valueColor} strokeWidth = {width} strokeDasharray = {strokeDasharray} /> <text x = {radius + 'px'} y = {radius + 'px' }dy = {fontSize/3 + 'px'} textAnchor = 'middle' fill = {valueColor} fontSize = {fontSize + 'px'} > {~~(value * 1000 / total) / 10}% </text> </svg> <div style = {{ marginTop: '10px' }}> {text} </div> </div> ); } } DonutChart.defaultProps = { holeSize : 0.8, radius : 65, backgroundColor : '#d1d8e7', valueColor : '#49649f' }; 

What should be the result


Codepen Collection


We configure assembly Webpack th


Basic Webpack config
 var webpack = require('webpack'); module.exports = { output: { path: './dist' }, resolve: { extensions: ['', '.js'], }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: [ 'latest', 'stage-0', 'react' ], plugins: [ 'transform-react-remove-prop-types', 'transform-react-constant-elements' ] } } ] }, plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': "'production'" }), new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.AggressiveMergingPlugin(), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, comments: false, sourceMap: true, mangle: true, minimize: true }) ] }; 

We add in package.json scripts for the assembly of the project

 "scripts": { "build:preact": "node ./scripts/build-as-preact-component.js", "build:react": "node ./scripts/build-as-react-component.js", "build:webcomponent": "node ./scripts/build-as-web-component.js", "build:vanila": "node ./scripts/build-as-vanila-component.js", "build:jquery": "node ./scripts/build-as-jquery-component", "build:angular": "node ./scripts/build-as-angular-component", "build": "npm run build:preact && npm run build:react && npm run build:webcomponent && npm run build:vanila && npm run build:jquery && npm run build:angular" } 

Building a Webpack and Web Components Wrapper


Modification of the basic Webpack-config and build
 var webpack = require('webpack'); var config = require('./webpack.config'); var statsConfig = require('./statsConfig'); config.resolve.alias = { 'react': 'preact-compat', 'react-dom': 'preact-compat' }; config.entry = './src/DonutChartWebComponent.js'; config.output.filename = 'DonutChartWebComponent.js'; webpack(config).run(function (err, stats) { console.log(stats.toString(statsConfig)); }); 

Wrapper


 import React from 'react'; import ReactDOM from 'react-dom'; import DonutChart from './DonutChart'; const proto = Object.create(HTMLElement.prototype, { attachedCallback: { value: function() { const mountPoint = document.createElement('span'); this.createShadowRoot().appendChild(mountPoint); const props = { radius : +this.getAttribute('radius') || undefined, holeSize : +this.getAttribute('hole-size') || undefined, text : this.getAttribute('text') || undefined, value : +this.getAttribute('value') || undefined, total : +this.getAttribute('total') || undefined, backgroundColor : this.getAttribute('background-color') || undefined, valueColor : this.getAttribute('value-color') || undefined }; ReactDOM.render(( <DonutChart {...props}/> ), mountPoint); } } }); document.registerElement('donut-chart', {prototype: proto}); 

Usage example


 <donut-chart value="39.6" total="100" text="Hello Web Components"></donut-chart> 

Result



Webpack build and Angular wrapper


Modification of the basic Webpack-config and build
 var webpack = require('webpack'); var config = require('./webpack.config'); var statsConfig = require('./statsConfig'); config.resolve.alias = { 'react': 'preact-compat', 'react-dom': 'preact-compat' }; config.entry = './src/DonutChartAngularComponent.js'; config.output.filename = 'DonutChartAngularComponent.js'; config.output.library = 'DonutChart'; config.output.libraryTarget = 'umd'; webpack(config).run(function (err, stats) { console.log(stats.toString(statsConfig)); }); 

Wrapper


 import React from 'react'; import ReactDOM from 'react-dom'; import DonutChart from './DonutChart'; const module = angular.module('future-charts-example', []); module.directive('donutChart', function() { return { restrict: 'E', link: function(scope, element, attrs) { const props = { radius : +attrs['radius'] || undefined, holeSize : +attrs['hole-size'] || undefined, text : attrs['text'] || undefined, value : +attrs['value'] || undefined, total : +attrs['total'] || undefined, backgroundColor : attrs['background-color'] || undefined, valueColor : attrs['value-color'] || undefined }; ReactDOM.render(( <DonutChart {...props}/> ), element[0]); } }; }); 

Usage example


 <body ng-app="future-charts-example"> <donut-chart value="89.6" total="100" text="Hello Angular"></donut-chart> </body> 

Result



Webpack build and jQuery wrapper


Modification of the basic Webpack-config and build
 var webpack = require('webpack'); var config = require('./webpack.config'); var statsConfig = require('./statsConfig'); config.resolve.alias = { 'react': 'preact-compat', 'react-dom': 'preact-compat' }; config.entry = './src/DonutChartJQueryComponent.js'; config.output.filename = 'DonutChartJQueryComponent.js'; config.output.library = 'DonutChart'; config.output.libraryTarget = 'umd'; webpack(config).run(function (err, stats) { console.log(stats.toString(statsConfig)); }); 

Wrapper


 import React from 'react'; import ReactDOM from 'react-dom'; import DonutChart from './DonutChart'; jQuery.fn.extend({ DonutChart: function(props) { this.each( function () { ReactDOM.render(( <DonutChart {...props}/> ), this); } ); } }); 

Usage example


 $('#app').DonutChart({ value : 42.1, total : 100, text : 'Hello jQuery' }); 

Result



Webpack build and wrapper for VanilaJS (use from native function)


Modification of the basic Webpack-config and build
 var webpack = require('webpack'); var config = require('./webpack.config'); var statsConfig = require('./statsConfig'); config.resolve.alias = { 'react': 'preact-compat', 'react-dom': 'preact-compat' }; config.entry = './src/DonutChartVanilaComponent.js'; config.output.filename = 'DonutChartVanilaComponent.js'; config.output.library = 'DonutChart'; config.output.libraryTarget = 'umd'; webpack(config).run(function (err, stats) { console.log(stats.toString(statsConfig)); }); 

Wrapper


 import React from 'react'; import ReactDOM from 'react-dom'; import DonutChart from './DonutChart'; module.exports = function DonutChartVanilaComponent(mountPoint, props) { ReactDOM.render(( <DonutChart {...props}/> ), mountPoint); }; 

Usage example


 DonutChart(document.getElementById('app'), { value : 57.4, total : 100, text : 'Hello Vanila' }); 

Result



Building a Webpack for React


Modification of the basic Webpack-config and build
 var webpack = require('webpack'); var config = require('./webpack.config'); var statsConfig = require('./statsConfig'); var react = { root: 'React', commonjs2: 'react', commonjs: 'react' }; var reactDom = { root: 'ReactDOM', commonjs2: 'react-dom', commonjs: 'react-dom' }; config.externals = { 'react': react, 'react-dom': reactDom }; config.entry = './src/DonutChartUMD.js'; config.output.filename = 'DonutChartReact.js'; config.output.library = 'DonutChart'; config.output.libraryTarget = 'umd'; webpack(config).run(function (err, stats) { console.log(stats.toString(statsConfig)); }); 

Result



Building a Webpack for Preact


Modification of the basic Webpack-config and build
 var webpack = require('webpack'); var config = require('./webpack.config'); var statsConfig = require('./statsConfig'); var preactCompat = { root: 'preactCompat', commonjs2: 'preact-compat', commonjs: 'preact-compat' }; config.externals = { 'react': preactCompat, 'react-dom': preactCompat }; config.entry = './src/DonutChartUMD.js'; config.output.filename = 'DonutChartPreact.js'; config.output.library = 'DonutChart'; config.output.libraryTarget = 'umd'; webpack(config).run(function (err, stats) { console.log(stats.toString(statsConfig)); }); 

Result



Conclusion


How much will each option weigh in the end:


ReactPreactVanilaJSjQueryAngularWeb Components
Component code (3kb)Component code (3kb)Component code (3kb)Component code (3kb)Component code (3kb)Component code (3kb)
Wrap (1kb)Wrap (1kb)Wrap (1kb)Wrap (1kb)
preact.min.js (3kb)preact.min.js (3kb)preact.min.js (3kb)preact.min.js (3kb)
preact-compat.min.js (18kb)preact-compat.min.js (18kb)preact-compat.min.js (18kb)preact-compat.min.js (18kb)
3kb3kb25kb25kb25kb25kb

An overhead of 20 kilobytes for the ability to use React components in any other frameworks or as Web Components is an excellent result. If you are developing some React-components, know - you can make them available to everyone and everyone - this is very simple. I hope that this tutorial will help to make the world at least a little bit better and reduce the terrible fragmentation of the universe of JavaScript development.


Sources: Github , Codepen , NPM


')

Source: https://habr.com/ru/post/316358/


All Articles