├── .babelrc ├── .postcssrc ├── src ├── assets │ └── scss │ │ ├── settings │ │ └── _spacings.scss │ │ └── generic │ │ └── _base.scss ├── main.js ├── utils │ └── external-component.js └── App.vue ├── server ├── components │ └── MyComponent │ │ └── MyComponent.vue └── index.js ├── public ├── favicon.ico └── index.html ├── .editorconfig ├── vue.config.js ├── .gitignore ├── .eslintrc.js ├── README.md └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@vue/app" 4 | ] 5 | } -------------------------------------------------------------------------------- /.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "autoprefixer": {} 4 | } 5 | } -------------------------------------------------------------------------------- /src/assets/scss/settings/_spacings.scss: -------------------------------------------------------------------------------- 1 | @import '~@avalanche/setting-spacings'; 2 | -------------------------------------------------------------------------------- /server/components/MyComponent/MyComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maoberlehner/distributed-vue-applications-loading-components-via-http/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import App from './App.vue'; 4 | 5 | Vue.config.productionTip = false; 6 | 7 | new Vue({ 8 | render: h => h(App), 9 | }).$mount(`#app`); 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const nodeSassMagicImporter = require(`node-sass-magic-importer`); 2 | 3 | module.exports = { 4 | css: { 5 | loaderOptions: { 6 | sass: { 7 | importer: nodeSassMagicImporter(), 8 | }, 9 | }, 10 | }, 11 | lintOnSave: false, 12 | }; 13 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require(`express`); 2 | const path = require(`path`); 3 | 4 | const PORT = 8200; 5 | 6 | const app = express(); 7 | 8 | app.use(express.static(path.resolve(__dirname, `components`), { 9 | maxAge: `365d`, 10 | })); 11 | 12 | app.listen(PORT); 13 | 14 | // eslint-disable-next-line no-console 15 | console.log(`Listening on: http://localhost:${PORT}`); 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.log 5 | *.orig 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.tgz 10 | *.vi 11 | *.zip 12 | *~ 13 | 14 | # OS or Editor folders 15 | ._* 16 | .cache 17 | .DS_Store 18 | .idea 19 | .project 20 | .settings 21 | .tmproj 22 | *.esproj 23 | *.sublime-project 24 | *.sublime-workspace 25 | nbproject 26 | Thumbs.db 27 | 28 | # Folders to ignore 29 | dist/* 30 | node_modules 31 | 32 | # Files to ignore 33 | *.map 34 | *.min.js 35 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Distributed Vue.js Applications Part 1: Loading Components via HTTP 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/utils/external-component.js: -------------------------------------------------------------------------------- 1 | export default async function externalComponent(url) { 2 | const name = url.split(`/`).reverse()[0].match(/^(.*?)\.umd/)[1]; 3 | 4 | if (window[name]) return window[name]; 5 | 6 | window[name] = new Promise((resolve, reject) => { 7 | const script = document.createElement(`script`); 8 | script.async = true; 9 | script.addEventListener(`load`, () => { 10 | resolve(window[name]); 11 | }); 12 | script.addEventListener(`error`, () => { 13 | reject(new Error(`Error loading ${url}`)); 14 | }); 15 | script.src = url; 16 | document.head.appendChild(script); 17 | }); 18 | 19 | return window[name]; 20 | } 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | `plugin:vue/recommended`, 5 | `@avalanche/eslint-config`, 6 | ], 7 | rules: { 8 | 'no-console': process.env.NODE_ENV === `production` ? `error` : `warn`, 9 | 'no-debugger': process.env.NODE_ENV === `production` ? `error` : `warn`, 10 | 'vue/component-name-in-template-casing': [`error`, 11 | `PascalCase`, 12 | ], 13 | 'vue/no-v-html': false, 14 | 'vue/html-closing-bracket-spacing': [`error`, { 15 | startTag: `never`, 16 | endTag: `never`, 17 | selfClosingTag: `never`, 18 | }], 19 | }, 20 | parserOptions: { 21 | parser: `babel-eslint`, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Distributed Vue.js Applications Part 1: Loading Components via HTTP 2 | 3 | [![Patreon](https://img.shields.io/badge/patreon-donate-blue.svg)](https://www.patreon.com/maoberlehner) 4 | [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://paypal.me/maoberlehner) 5 | 6 | This is an example project for the following article: [Distributed Vue.js Applications Part 1: Loading Components via HTTP](https://markus.oberlehner.net/blog/distributed-vue-applications-loading-components-via-http/) 7 | 8 | ## Build Setup 9 | 10 | ```bash 11 | # Install dependencies. 12 | npm install 13 | 14 | # Start the distribution server. 15 | node server/index.js 16 | 17 | # Serve the client application with hot reload. 18 | npm run serve 19 | 20 | # Build the client application for production with minification. 21 | npm run build 22 | ``` 23 | 24 | ## About 25 | 26 | ### Author 27 | 28 | Markus Oberlehner 29 | Website: https://markus.oberlehner.net 30 | Twitter: https://twitter.com/MaOberlehner 31 | PayPal.me: https://paypal.me/maoberlehner 32 | Patreon: https://www.patreon.com/maoberlehner 33 | 34 | ### License 35 | 36 | MIT 37 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | 35 | 36 | 48 | -------------------------------------------------------------------------------- /src/assets/scss/generic/_base.scss: -------------------------------------------------------------------------------- 1 | @import '~@avalanche/generic-box-sizing-reset'; 2 | @import '~reset-css/sass/reset'; 3 | 4 | // 1. Usually a larger line height is beneficial 5 | // for blocks of text, but we want to have a 6 | // rather small line height for our basic UI 7 | // elements so we can apply uniform spacings 8 | // using margins and paddings. We'll add an 9 | // extra CSS class with a larger line height 10 | // to apply to blocks of text. 11 | // 2. A kind of “dirty” white is more pleasing 12 | // to the eyes. 13 | // 3. A kind of “almost black but not quite black” 14 | // is also more pleasing to the eyes. 15 | body { 16 | line-height: 1.25; // 1 17 | background-color: #f9f9f9; // 2 18 | color: #222; // 3 19 | font-family: sans-serif; 20 | } 21 | 22 | // 1. Larger font sizes need less line height. 23 | h1, 24 | h2, 25 | h3, 26 | h4, 27 | h5 { 28 | line-height: 1.2; // 1 29 | font-weight: 700; 30 | } 31 | 32 | h1 { 33 | font-size: 2.027em; 34 | } 35 | 36 | h2 { 37 | font-size: 1.802em; 38 | } 39 | 40 | h3 { 41 | font-size: 1.602em; 42 | } 43 | 44 | h4 { 45 | font-size: 1.424em; 46 | } 47 | 48 | h5 { 49 | font-size: 1.266em; 50 | } 51 | 52 | h6 { 53 | font-size: 1.125em; 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "distributed-vue-applications-loading-components-via-http", 3 | "version": "0.1.0", 4 | "author": "Markus Oberlehner", 5 | "homepage": "https://github.com/maoberlehner/distributed-vue-applications-loading-components-via-http", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "serve": "vue-cli-service serve", 10 | "build": "vue-cli-service build", 11 | "lint": "vue-cli-service lint" 12 | }, 13 | "dependencies": { 14 | "@avalanche/generic-box-sizing-reset": "^4.0.0-alpha.5", 15 | "@avalanche/object-container": "^4.0.0-alpha.8", 16 | "@avalanche/object-vertical-spacing": "^4.0.0-alpha.5", 17 | "@avalanche/setting-spacings": "^4.0.0-alpha.6", 18 | "@vue/cli-plugin-babel": "^3.5.5", 19 | "@vue/cli-service": "^3.5.3", 20 | "express": "^4.16.4", 21 | "node-sass": "^4.11.0", 22 | "node-sass-magic-importer": "^5.3.1", 23 | "reset-css": "^4.0.1", 24 | "sass-loader": "^7.1.0", 25 | "vue": "^2.6.10", 26 | "vue-template-compiler": "^2.6.10" 27 | }, 28 | "devDependencies": { 29 | "@avalanche/eslint-config": "^3.0.0", 30 | "@vue/cli-plugin-eslint": "^3.5.1", 31 | "eslint-plugin-import": "^2.16.0", 32 | "eslint-plugin-vue": "^5.2.2" 33 | }, 34 | "browserslist": [ 35 | ">0.25%", 36 | "not ie 10", 37 | "not op_mini all" 38 | ] 39 | } 40 | --------------------------------------------------------------------------------