рдпрд╣ рд▓реЗрдЦ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдЙрдкрд▓рдмреНрдз рдЙрдкрдХрд░рдгреЛрдВ рдХреЛ рдПрдХ рд╕рд╛рде рд▓рд╛рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рд╣реИ рдФрд░ рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рддрд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рд╡реЗрдмрдкреИрдХ рдЬреИрд╕реЗ рдХрд▓реЗрдХреНрдЯрд░реЛрдВ рджреНрд╡рд╛рд░рд╛ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕рдВрдХрд▓рди рдХреЗ рдмрд┐рдирд╛ рд░рд┐рдПрдХреНрдЯ рдкрд░ рдЙрддреНрдкрд╛рджрди рддреИрдпрд╛рд░ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЛ рдмрдирд╛рдирд╛ рд╕рдВрднрд╡ рд╣реИ рдпрд╛ рдХрдо рд╕реЗ рдХрдо рдЗрд╕ рддрд░рд╣ рдХреЗ рд╕рдВрдХрд▓рди рдХреЛ рдХрдо рд╕реЗ рдХрдо рдХрд░рдирд╛ рд╣реИред
рд╡рд░реНрдгрд┐рдд рд╕рдм рдХреБрдЫ рдмрд╣реБрдд рдкреНрд░рдпреЛрдЧрд╛рддреНрдордХ рд╣реИ рдФрд░ рдореИрдВрдиреЗ рд╕реНрдерд╛рдиреЛрдВ рдореЗрдВ рдЬрд╛рдирдмреВрдЭрдХрд░ рдХреЛрдиреЛрдВ рдХреЛ рдХрд╛рдЯ рджрд┐рдпрд╛ рд╣реИред рдХрд┐рд╕реА рднреА рдорд╛рдорд▓реЗ рдореЗрдВ рдореИрдВ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЙрддреНрдкрд╛рджрди рдкрд░ рдРрд╕рд╛ рдХреБрдЫ рдХрд░рдиреЗ рдХреА рд╕рд▓рд╛рд╣ рдирд╣реАрдВ рджреЗрддрд╛ред
ECMAScript рдореЙрдбреНрдпреВрд▓ ( <script type="module"/>
рдлреЙрд░реНрдо import Foo from './foo';
рдФрд░ import('./Foo')
) рдХреЗ рд╕рд╛рде рд╕реАрдзреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдирдИ рдирд╣реАрдВ рд╣реИ, рдпрд╣ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рд╕рдорд░реНрдерд┐рдд рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд╣реИ: https: //caniuse.com/#feat=es6-module
рд▓реЗрдХрд┐рди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рд╣рдо рди рдХреЗрд╡рд▓ рд╣рдорд╛рд░реЗ рдореЙрдбреНрдпреВрд▓, рдмрд▓реНрдХрд┐ рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЛ рднреА рдЖрдпрд╛рдд рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕ рд╡рд┐рд╖рдп рдкрд░ рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рд▓реЗрдЦ рд╣реИ: https://salomvary.com/es6-modules-in-browsers.html ред рдФрд░ рдЙрд▓реНрд▓реЗрдЦ рдХреЗ рд▓рд╛рдпрдХ рдПрдХ рдФрд░ рд╕рдорд╛рди рд░реВрдк рд╕реЗ рдЕрдЪреНрдЫрд╛ рд▓реЗрдЦ https://github.com/stken2050/esm-bundlerless рд╣реИ ред
рдЗрди рд▓реЗрдЦреЛрдВ рд╕реЗ рдЕрдиреНрдп рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рддреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпреЗ рдмрд┐рдВрджреБ рдПрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИрдВ:
- рдкреИрдХреЗрдЬ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдЖрдпрд╛рдд (рдпрд╛ рдЖрдпрд╛рдд рдирдХреНрд╢реЗ) рдХреЗ рд▓рд┐рдП рд╕рдорд░реНрдерди: рдЬрдм рд╣рдо
import React from 'react'
рд▓рд┐рдЦрддреЗ import React from 'react'
рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣рдореЗрдВ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдХреБрдЫ рдЖрдпрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП https://cdn.com/react/react.production.js
- рдпреВрдПрдордбреА рдХреЗ рд▓рд┐рдП рд╕рдорд░реНрдерди: рд░рд┐рдПрдХреНрдЯ рдХреЛ рдЕрднреА рднреА рдпреВрдПрдордбреА рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд┐рддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдлрд┐рд▓рд╣рд╛рд▓ рд▓реЗрдЦрдХ рдЕрднреА рддрдХ рдЗрд╕ рдмрд╛рдд рдкрд░ рд╕рд╣рдордд рдирд╣реАрдВ рд╣реБрдП рд╣реИрдВ рдХрд┐ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреЛ рдореЙрдбреНрдпреВрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдХреИрд╕реЗ рд╡рд┐рддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПред
- JSX
- рд╕реАрдПрд╕рдПрд╕ рдЖрдпрд╛рдд
рдЖрдЗрдП рдмрд╛рд░реА-рдмрд╛рд░реА рд╕реЗ рд╕рднреА рдмрд┐рдВрджреБрдУрдВ рдкрд░ рдЬрд╛рдПрдВред
рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреА рд╕рдВрд░рдЪрдирд╛
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреА рд╕рдВрд░рдЪрдирд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░реЗрдВрдЧреЗ:
node_modules
рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдпрд╣ рд╡рд╣ рдЬрдЧрд╣ рд╣реИ рдЬрд╣рд╛рдВ рдкрд░ рдирд┐рд░реНрднрд░рддрд╛рдПрдВ рдбрд╛рд▓реА рдЬрд╛рдПрдВрдЧреАindex*.html
рдФрд░ рд╕реЗрд╡рд╛ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреЗ рд╕рд╛рде src
рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛
app
рд╕реАрдзреЗ рд░рд┐рдПрдХреНрдЯ рдкрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛрдб
рдкреИрдХреЗрдЬ рд╡рд┐рдирд┐рд░реНрджреЗрд╢рдХ рдЖрдпрд╛рдд рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ
import React from 'react';
рд╕реЗ import React from 'react';
рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рдорд╛рдзреНрдпрдо import React from 'react';
рд╣рдореЗрдВ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдХреЛ рдмрддрд╛рдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕реНрд░реЛрдд рдХреА рддрд▓рд╛рд╢ рдХрд╣рд╛рдБ рдХрд░реЗрдВ, рдХреНрдпреЛрдВрдХрд┐ react
рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдлрд╝рд╛рдЗрд▓ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реВрдЪрдХ рд╣реИред рдЗрд╕ https://github.com/guybedford/es-module-shims рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реНрдЯрдм рд╣реИред
рдЪрд▓реЛ рд╕реНрдЯрдм рдФрд░ рд░рд┐рдПрдХреНрдЯ рд╕реЗрдЯ рдХрд░реЗрдВ:
$ npm i es-module-shims react react-dom --save
рд╣рдо рдлрд╝рд╛рдЗрд▓ рдХреЛ public/index-dev.html
рд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВрдЧреЗ:
<!DOCTYPE html> <html> <body> <div id="root"></div> <script defer src="../node_modules/es-module-shims/dist/es-module-shims.js"></script> <script type="importmap-shim"> { "imports": { "react": "../node_modules/react/umd/react.development.js", "react-dom": "../node_modules/react-dom/umd/react-dom.development.js" } } </script> <script type="module-shim"> import './app/index.jsx'; </script> </body> </html>
рдЬрд╣рд╛рдБ src/app/index.jsx
рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; (async () => { const {Button} = await import('./Button.jsx'); const root = document.getElementById('root'); ReactDOM.render(( <div> <Button>Direct</Button> </div> ), root); })();
рдФрд░ src/app/Button.jsx
рдЗрд╕ рддрд░рд╣:
import React from 'react'; export const Button = ({children}) => <button>{children}</button>;
рдХреНрдпрд╛ рдпрд╣ рдХрд╛рдо рдХрд░реЗрдЧрд╛? рдмрд┐рд▓реНрдХреБрд▓ рдирд╣реАрдВред рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд╡рдЬреВрдж рдХрд┐ рдЬрд╣рд╛рдВ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рд╡рд╣рд╛рдВ рд╕реЗ рд╕рдм рдХреБрдЫ рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдЖрдпрд╛рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдЪрд▓рд┐рдП рдЕрдЧрд▓реА рд╕рдорд╕реНрдпрд╛ рдкрд░ рдЪрд▓рддреЗ рд╣реИрдВред
UMD рд╕рдорд░реНрдерди рдХрд░рддреЗ рд╣реИрдВ
рдЧрддрд┐рд╢реАрд▓ рддрд░реАрдХрд╛
рдЗрд╕ рддрдереНрдп рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдХрд┐ рд░рд┐рдПрдХреНрдЯ рдХреЛ рдпреВрдПрдордбреА рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд┐рддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕реЗ рд╕реАрдзреЗ рдЖрдпрд╛рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рд╕реНрдЯрдм рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рднреА (рдпрджрд┐ рдЯрд┐рдХрдЯ рдХреЛ рдорд░рдореНрдордд рдХреЗ рд░реВрдк рдореЗрдВ рдмрдВрдж рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рддреЛ рдЖрдк рдХрджрдо рдХреЛ рдЫреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ)ред рд╣рдореЗрдВ рдХрд┐рд╕реА рддрд░рд╣ рд╕реНрд░реЛрдд рдХреЛ рдкреИрдЪ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рддрд╛рдХрд┐ рдпрд╣ рд╕рдВрдЧрдд рд╣реЛ рдЬрд╛рдПред
рдЙрдкрд░реЛрдХреНрдд рд▓реЗрдЦреЛрдВ рдиреЗ рдореБрдЭреЗ рдЗрд╕рдХреЗ рд▓рд┐рдП рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░реЗрд░рд┐рдд рдХрд┐рдпрд╛, рдЬреЛ рдиреЗрдЯрд╡рд░реНрдХ рдЕрдиреБрд░реЛрдзреЛрдВ рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЛ рдмрд╛рдзрд┐рдд рдФрд░ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЪрд▓рд┐рдП рдореБрдЦреНрдп рдПрдВрдЯреНрд░реА рдкреЙрдЗрдВрдЯ src/index.js
рдмрдирд╛рддреЗ рд╣реИрдВ, рдЬрд╣рд╛рдБ рд╣рдо SW рдФрд░ App рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВрдЧреЗ рдФрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди ( src/app/index.jsx
) рдХреЛ рд╕реАрдзреЗ рдЗрдирд╡реЙрдЗрд╕ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ:
(async () => { try { const registration = await navigator.serviceWorker.register('sw.js'); await navigator.serviceWorker.ready; const launch = async () => import("./app/index.jsx"); // SW // https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim if (navigator.serviceWorker.controller) { await launch(); } else { navigator.serviceWorker.addEventListener('controllerchange', launch); } } catch (error) { console.error('Service worker registration failed', error); } })();
рдПрдХ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдмрдирд╛рдПрдБ ( src/sw.js
):
// //@see https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim self.addEventListener('activate', event => event.waitUntil(clients.claim())); const globalMap = { 'react': 'React', 'react-dom': 'ReactDOM' }; const getGlobalByUrl = (url) => Object.keys(globalMap).reduce((res, key) => { if (res) return res; if (matchUrl(url, key)) return globalMap[key]; return res; }, null); const matchUrl = (url, key) => url.includes(`/${key}/`); self.addEventListener('fetch', (event) => { const {request: {url}} = event; console.log('Req', url); const fileName = url.split('/').pop(); const ext = fileName.includes('.') ? url.split('.').pop() : ''; if (!ext && !url.endsWith('/')) { url = url + '.jsx'; } if (globalMap && Object.keys(globalMap).some(key => matchUrl(url, key))) { event.respondWith( fetch(url) .then(response => response.text()) .then(body => new Response(` const head = document.getElementsByTagName('head')[0]; const script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.appendChild(document.createTextNode( ${JSON.stringify(body)} )); head.appendChild(script); export default window.${getGlobalByUrl(url)}; `, { headers: new Headers({ 'Content-Type': 'application/javascript' }) }) ) ) } else if (url.endsWith('.js')) { // rewrite for import('./Panel') with no extension event.respondWith( fetch(url) .then(response => response.text()) .then(body => new Response( body, { headers: new Headers({ 'Content-Type': 'application/javascript' }) }) ) ) } });
рдЬреЛ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдЙрд╕реЗ рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рдмрддрд╛рдиреЗ рдХреЗ рд▓рд┐рдП:
- рд╣рдордиреЗ рдПрдХ рдирд┐рд░реНрдпрд╛рдд рдорд╛рдирдЪрд┐рддреНрд░ рдмрдирд╛рдпрд╛ рд╣реИ рдЬреЛ рдПрдХ рдкреИрдХреЗрдЬ рдирд╛рдо рдХреЛ рдПрдХ рд╡реИрд╢реНрд╡рд┐рдХ рдЪрд░ рдХреЗ рд╕рд╛рде рдЬреЛрдбрд╝рддрд╛ рд╣реИ
- UMD рдореЗрдВ рд▓рд┐рдкрд┐ рдЧрдИ рд╕рд╛рдордЧреНрд░реА рдХреА рд╕рд╛рдордЧреНрд░реА рдХреЗ рд╕рд╛рде
head
рдореЗрдВ рдПрдХ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЯреИрдЧ рдмрдирд╛рдПрдВ - рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдирд┐рд░реНрдпрд╛рдд рдХреЗ рд░реВрдк рдореЗрдВ рд╡реИрд╢реНрд╡рд┐рдХ рдЪрд░ рдирд┐рд░реНрдпрд╛рдд рдХрд┐рдпрд╛ рдЧрдпрд╛
рдПрдХ рдбреЗрдореЛ рдХреЗ рд▓рд┐рдП, рдЗрд╕ рддрд░рд╣ рдХреЗ рдПрдХ рдХреНрд░реВрд░ рдкреИрдЪ рдкрд░реНрдпрд╛рдкреНрдд рд╣реЛрдЧрд╛, рд▓реЗрдХрд┐рди рдпрд╣ рд╕рднреА рдпреВрдПрдордбреА рдЖрд╡рд░рдг рдХреЗ рд╕рд╛рде рдХрд╛рдо рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдмрджрд▓реЗ рдореЗрдВ рдХреБрдЫ рдЕрдзрд┐рдХ рд╡рд┐рд╢реНрд╡рд╕рдиреАрдп рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдЕрдм рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП src/index-dev.html
рдмрджрд▓реЗрдВ:
<!DOCTYPE html> <html> <body> <div id="root"></div> <script defer src="../node_modules/es-module-shims/dist/es-module-shims.js"></script> <script type="importmap-shim">... </script> <script type="module-shim" src="index.js"></script> </body> </html>
рдЕрдм рд╣рдо React рдФрд░ React DOM рдХреЛ рдЗрдореНрдкреЛрд░реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рд╕реНрдереИрддрд┐рдХ рдкрде
рдпрд╣ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рд╣реИред рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд╛ рдПрдХ рдЕрдиреМрдкрдЪрд╛рд░рд┐рдХ ES рдирд┐рд░реНрдорд╛рдг рд╣реИ:
npm install esm-react --save
рдЖрдпрд╛рдд рдорд╛рдирдЪрд┐рддреНрд░ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
{ "imports": { "react": "../node_modules/esm-react/src/react.js", "react-dom": "../node_modules/esm-react/src/react-dom.js" } }
рд▓реЗрдХрд┐рди рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдмрд╣реБрдд рдкреАрдЫреЗ рд╣реИ, рдирд╡реАрдирддрдо рд╕рдВрд╕реНрдХрд░рдг 16.8.3
рдЬрдмрдХрд┐ рд░рд┐рдПрдХреНрдЯ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА 16.10.2
ред
JSX
JSX рдХреЛ рд╕рдВрдХрд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рджреЛ рддрд░реАрдХреЗ рд╣реИрдВред рд╣рдо рдпрд╛ рддреЛ рдХрдВрд╕реЛрд▓ рд╕реЗ рдкрд╛рд░рдВрдкрд░рд┐рдХ рдмреИрдмреЗрд▓ рдХреЛ рдкреВрд░реНрд╡-рдЗрдХрдЯреНрдард╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдпрд╛ рдЗрд╕реЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рд░рдирдЯрд╛рдЗрдо рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдЙрддреНрдкрд╛рджрди рдХреЗ рд▓рд┐рдП, рдкреВрд░реНрд╡-рд╕рдВрдХрд▓рди рдмреЗрд╣рддрд░ рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд┐рдХрд╛рд╕ рдореЛрдб рдореЗрдВ рдпрд╣ рд░рдирдЯрд╛рдЗрдо рдореЗрдВ рд╕рдВрднрд╡ рд╣реИред рдЪреВрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдПрдХ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рд╣реИ, рд╣рдо рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред
рдмреЗрдмрд▓ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд┐рд╢реЗрд╖ рдкреИрдХреЗрдЬ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ:
$ npm install @babel/standalone --save-dev
рдЕрдм рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ ( src/sw.js
) рдХреЗ рд╕рд╛рде рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЬреЛрдбрд╝реЗрдВ:
# src/sw.js // importScripts('../node_modules/@babel/standalone/babel.js'); // self.addEventListener('fetch', (event) => { // } else if (url.endsWith('.jsx')) { event.respondWith( fetch(url) .then(response => response.text()) .then(body => new Response( //TODO Babel.transform(body, { presets: [ 'react', ], plugins: [ 'syntax-dynamic-import' ], sourceMaps: true }).code, { headers: new Headers({ 'Content-Type': 'application/javascript' }) }) ) ) } });
рдпрд╣рд╛рдВ рд╣рдордиреЗ рдиреЗрдЯрд╡рд░реНрдХ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯ рдХрд░рдиреЗ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрдиреЗ рдХреЗ рд╕рд╛рде рдПрдХ рд╣реА рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛, рд╣рдордиреЗ рдмреЗрдмрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдореВрд▓ рд╕реНрд░реЛрдд рдХреЛрдб рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ред рдХреГрдкрдпрд╛ рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдбрд╛рдпрдиреЗрдорд┐рдХ рдЖрдпрд╛рддреЛрдВ рдХреЗ рд▓рд┐рдП рдкреНрд▓рдЧрдЗрди рдХреЛ syntax-dynamic-import
рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рди рдХрд┐ рд╣рдореЗрд╢рд╛ рдХреА рддрд░рд╣ @babel/plugin-syntax-dynamic-import
рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдПрдХ рд╕реНрдЯреИрдВрдбрдЕрд▓реЛрди рд╕рдВрд╕реНрдХрд░рдг рд╣реИред
рд╕реАрдПрд╕рдПрд╕
рдЙрд▓реНрд▓рд┐рдЦрд┐рдд рд▓реЗрдЦ рдореЗрдВ, рд▓реЗрдЦрдХ рдиреЗ рдкрд╛рда рдкрд░рд┐рд╡рд░реНрддрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рд╣реИ, рд╣рдо рдкреГрд╖реНрда рдкрд░ рдереЛрдбрд╝рд╛ рдЖрдЧреЗ рдЬрд╛рдПрдВрдЧреЗ рдФрд░ рд╕реАрдПрд╕рдПрд╕ рдХреЛ рдПрдореНрдмреЗрдб рдХрд░реЗрдВрдЧреЗред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдлрд┐рд░ рд╕реЗ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ ( src/sw.js
) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ:
// self.addEventListener('fetch', (event) => { // + } else if (url.endsWith('.css')) { event.respondWith( fetch(url) .then(response => response.text()) .then(body => new Response( ` const head = document.getElementsByTagName('head')[0]; const style = document.createElement('style'); style.setAttribute('type', 'text/css'); style.appendChild(document.createTextNode( ${JSON.stringify(body)} )); head.appendChild(style); export default null; `, { headers: new Headers({ 'Content-Type': 'application/javascript' }) }) ) ); } });
рджреЗрдЦрд╛! рдпрджрд┐ рд╣рдо рдЕрдм рдПрдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ src/index-dev.html
, рддреЛ рд╣рдо рдмрдЯрди рджреЗрдЦреЗрдВрдЧреЗред рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдЖрд╡рд╢реНрдпрдХ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рд╕реНрдерд╛рдкрд┐рдд рд╣реИ рдФрд░ рдХрд┐рд╕реА рднреА рдЪреАрдЬрд╝ рдХреЗ рд╡рд┐рд░реЛрдз рдореЗрдВ рдирд╣реАрдВ рд╣реИред рдпрджрд┐ рдЖрдкрдХреЛ рдпрдХреАрди рдирд╣реАрдВ рд╣реИ, рддреЛ рдмрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЖрдк Dev Tools рдЦреЛрд▓ рд╕рдХрддреЗ рд╣реИрдВ, Application
рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ, рд╡рд╣рд╛рдВ Service Workers
, рдФрд░ рд╕рднреА рдкрдВрдЬреАрдХреГрдд рд╢реНрд░рдорд┐рдХреЛрдВ рдХреЗ рд▓рд┐рдП Unregister
рдХреНрд▓рд┐рдХ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдлрд┐рд░ рдкреГрд╖реНрда рдХреЛ рдкреБрдирдГ рд▓реЛрдб рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдкреНрд░реЛрдбрдХреНрд╢рдВрд╕
рдЙрдкрд░реЛрдХреНрдд рдХреЛрдб рд╡рд┐рдХрд╛рд╕ рдореЛрдб рдореЗрдВ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╕реНрд╡рдпрдВ рд╣рдо рд╕рд╛рдЗрдЯ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдЕрдкрдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдХреЛрдб рд╕рдВрдХрд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдордЬрдмреВрд░ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдпрд╣ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЕрд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд╣реИред рдЪрд▓реЛ рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХрд╛ рдиреНрдпреВрдирддрд░ рдЙрддреНрдкрд╛рджрди рдореЛрдб рдмрдирд╛рддреЗ рд╣реИрдВред
рдПрдХ рдЕрд▓рдЧ src/index.html
рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдмрд┐рдВрджреБ рдмрдирд╛рдПрдБ:
<!DOCTYPE html> <html> <body> <div id="root"></div> <script type="module" src="index.js"></script> </body> </html>
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдпрд╣рд╛рдВ рдХреЛрдИ рд╕реНрдЯрдмреНрд╕ рдирд╣реАрдВ рд╣реИрдВ, рд╣рдо рдкреИрдХреЗрдЬ рдирд╛рдореЛрдВ рдХреЗ рдкреБрдирд░реНрд▓реЗрдЦрди рдХреЗ рд▓рд┐рдП рдПрдХ рдФрд░ рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рдЪреВрдБрдХрд┐ рд╣рдореЗрдВ importMap.json
рдХреЛ рдлрд┐рд░ рд╕реЗ рд╕рдВрдХрд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмреИрдмреЗрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ importMap.json
рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП importMap.json
рдмрдЬрд╛рдп рдкрде рдХреЛ importMap.json
рд╕реЗ importMap.json
рд▓рд┐рдП рдХрд░реЗрдВрдЧреЗред рдЖрд╡рд╢реНрдпрдХ рдкреИрдХреЗрдЬ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ:
$ npm install @babel/cli @babel/core @babel/preset-react @babel/plugin-syntax-dynamic-import babel-plugin-module-resolver --save-dev
рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреЗ рд╕рд╛рде рдПрдХ рдЕрдиреБрднрд╛рдЧ рдХреЛ package.json
рдЬреЛрдбрд╝реЗрдВ:
{ "scripts": { "start": "npm run build -- --watch", "build": "babel src/app --out-dir build/app --source-maps --copy-files" } }
.babelrc.js
рдлрд╝рд╛рдЗрд▓ рдЬреЛрдбрд╝реЗрдВ:
module.exports = { presets: [ '@babel/preset-react' ], plugins: [ '@babel/plugin-syntax-dynamic-import', [ 'babel-plugin-module-resolver', { alias: { 'react': './node_modules/react/umd/react.development.js', 'react-dom': './node_modules/react-dom/umd/react-dom.development.js' }, // build resolvePath: (sourcePath, currentFile, opts) => resolvePath(sourcePath, currentFile, opts).replace('../../', '../') } ] ] }
', module.exports = { presets: [ '@babel/preset-react' ], plugins: [ '@babel/plugin-syntax-dynamic-import', [ 'babel-plugin-module-resolver', { alias: { 'react': './node_modules/react/umd/react.development.js', 'react-dom': './node_modules/react-dom/umd/react-dom.development.js' }, // build resolvePath: (sourcePath, currentFile, opts) => resolvePath(sourcePath, currentFile, opts).replace('../../', '../') } ] ] }
рдпрд╣ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рдпрд╣ рдлрд╝рд╛рдЗрд▓ рдХреЗрд╡рд▓ рдЙрддреНрдкрд╛рджрди рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХреА рдЬрд╛рдПрдЧреА, рд╡рд┐рдХрд╛рд╕ рдореЛрдб рдореЗрдВ рд╣рдо рд╕рд░реНрд╡рд┐рд╕ рд╡рд░реНрдХрд░ рдореЗрдВ рдмреИрдмреЗрд▓ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреЗ рд╣реИрдВред
рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП рдпреБрджреНрдз рдореЛрдб рдЬреЛрдбрд╝реЗрдВ:
// src/index.js if ('serviceWorker' in navigator) { (async () => { try { // const production = !window.location.toString().includes('index-dev.html'); const config = { globalMap: { 'react': 'React', 'react-dom': 'ReactDOM' }, production }; const registration = await navigator.serviceWorker.register('sw.js?' + JSON.stringify(config)); await navigator.serviceWorker.ready; const launch = async () => { if (production) { await import("./app/index.js"); } else { await import("./app/index.jsx"); } }; // https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim if (navigator.serviceWorker.controller) { await launch(); } else { navigator.serviceWorker.addEventListener('controllerchange', launch); } } catch (error) { console.error('Service worker registration failed', error); } })(); } else { alert('Service Worker is not supported'); }
рд╢рд░реНрддреЛрдВ рдХреЛ src/sw.js
:
// src/sw.js const {globalMap, production} = JSON.parse((decodeURIComponent(self.location.search) || '?{}').substr(1)); if (!production) importScripts('../node_modules/@babel/standalone/babel.js');
рдХреА рдЬрдЧрд╣
// src/sw.js if (!ext && !url.endsWith('/')) { url = url + '.jsx' with }
рдкрд░
// src/sw.js if (!ext && !url.endsWith('/')) { url = url + '.' + (production ? 'js' : 'jsx'); }
рдЪрд▓реЛ рдПрдХ рдЫреЛрдЯрд╛ рд╕рд╛ рдХрдВрд╕реЛрд▓ рд╕реНрдХреНрд░рд┐рдкреНрдЯ build.sh
рдмрдирд╛рддреЗ рд╣реИрдВред рдПрд╕ (рд╡рд┐рдВрдбреЛрдЬ рд╡рд╛рд▓реЗ рд▓реЛрдЧ рдЫрд╡рд┐ рдФрд░ рд╕рдорд╛рдирддрд╛ рдореЗрдВ рд╡рд┐рдВрдбреЛрдЬ рдХреЗ рд▓рд┐рдП рдПрдХ рд╣реА рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ) рдЬреЛ рдЖрдкрдХреЛ build
рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдореЗрдВ рдЖрд╡рд╢реНрдпрдХ рд╕рднреА рдЪреАрдЬреЗрдВ рдПрдХрддреНрд░ рдХрд░реЗрдЧрд╛:
# rm -rf build # mkdir -p build/scripts mkdir -p build/node_modules # cp -r ./node_modules/react ./build/node_modules/react cp -r ./node_modules/react-dom ./build/node_modules/react-dom # , cp ./src/*.js ./build cp ./src/index.html ./build/index.html # npm run build
рд╣рдо рдЗрд╕ рддрд░рд╣ рд╕реЗ рдЪрд▓рддреЗ рд╣реИрдВ рддрд╛рдХрд┐ node_modules
рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдХреЗрд╡рд▓ рдирд┐рд░реНрдорд╛рдг рдЪрд░рдг рдФрд░ рд╡рд┐рдХрд╛рд╕ рдореЛрдб рдореЗрдВ рдЖрд╡рд╢реНрдпрдХ рдирд┐рд░реНрднрд░рддрд╛ рд╕реЗ рдЙрддреНрдкрд╛рджрди рдкрд░ рдкреНрд░рдлреБрд▓реНрд▓рд┐рдд рди рд╣реЛред
рдЕрдВрддрд┐рдо рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА: http://github.com/kirill-konshin/pure-react-with-dynamic-imports
рдпрджрд┐ рд╣рдо рдЕрдм build/index.html
рдЦреЛрд▓рддреЗ рд╣реИрдВ build/index.html
рддреЛ рд╣рдореЗрдВ src/index-dev.html
рдХреЗ рд╕рдорд╛рди рдЖрдЙрдЯрдкреБрдЯ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛ build/index.html
рд▓реЗрдХрд┐рди рдЗрд╕ рдмрд╛рд░ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдХреБрдЫ рднреА рдПрдХрддреНрд░ рдирд╣реАрдВ рдХрд░реЗрдЧрд╛, рдпрд╣ рдкрд╣рд▓реЗ рдмреИрдмрд▓ рджреНрд╡рд╛рд░рд╛ рд╕рдВрдЧреНрд░рд╣рд┐рдд рдлрд╝рд╛рдЗрд▓реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдЧрд╛ред
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рд╕рдорд╛рдзрд╛рди рдореЗрдВ рджреЛрд╣рд░рд╛рд╡ рд╣реИ: importMap.json
, .babelrc.js
рдлрд╝рд╛рдЗрд▓ рдХрд╛ alias
рдЕрдиреБрднрд╛рдЧ рдФрд░ build.sh
рдкрд░ рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рдПрдХ рд╕реВрдЪреАред рдпрд╣ рдПрдХ рдбреЗрдореЛ рдХреЗ рд▓рд┐рдП рдХрд░реЗрдЧрд╛, рд▓реЗрдХрд┐рди рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░ рдЗрд╕реЗ рдХрд┐рд╕реА рддрд░рд╣ рд╕реЗ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред
рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдпрд╣рд╛рдВ рдЙрдкрд▓рдмреНрдз рд╣реИ: https://kirill-konshin.imtqy.com/pure-react-with-dynamic-imports/index.html
рдирд┐рд╖реНрдХрд░реНрд╖
рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдПрдХ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╡реНрдпрд╡рд╣рд╛рд░реНрдп рдЙрддреНрдкрд╛рдж рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдмрд╣реБрдд рдХрдЪреНрдЪрд╛ред
HTTP2 рдХреЛ рдиреЗрдЯрд╡рд░реНрдХ рдкрд░ рднреЗрдЬреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рдЫреЛрдЯреА рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЗ рдПрдХ рд╕рдореВрд╣ рдХрд╛ рдзреНрдпрд╛рди рд░рдЦрдирд╛ рдЪрд╛рд╣рд┐рдПред
рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдЬрд╣рд╛рдВ рдЖрдк рдХреЛрдб рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ