Assumes you are comfortable with HTML, CSS, JS. This will cover creating SPAs with webpack and Vue basics. This will not cover mobile or advanced Vue. Improvements will make frontend development a more imperative programming approach as compared to the typical declarative methods found in more basic work.
Introduction
Once you understand the basics of browser languages, such as HTML, CSS, and JS, then you quickly desire to have more interactive elements. The obvious evolution is jQuery, as you can directly perform imperative programming to manipulate elements. That was a powerful web technology for about a decade, and it is still used today. But, frontend programmers quickly learned that jQuery code became extremely crowded, untestable, and overly-complex. There were limits to what could be practically built with jQuery. The upper limit was Single Page Applications (SPAs).
SPAs allow for much more dynamic and intuitive interfaces for users. However, to build and maintain them, more powerful technologies needed to perform scoped logic on modular components. The first two frameworks to support this were Angular and React. Vue came slightly later and was able to capitalize on some of the mistakes learned from the first two. This allowed for a powerful system and fast workflow.
This post will explain how to prototype dynamic user interfaces as well as provide an understanding of more advanced technologies used by frontend programmers.
Webpack and Modular Programming
Webpack builds a dependency graph and uses the graph to generate an optimized bundle where scripts will be executed in the correct order. In a more opinionated manner, it bundles javascript applications that are built using a modular design. Modular programming is used so that verification, debugging, and testing can be performed as an independent and isolated unit. They can then be added to other modules using a simple ES2015 import
or CommonJs require
statement.
There are a variety of different flavors of JS used for frontend development, including Coffescript, TypeScript and ESNext (Babel). These can be compiled into JS, then used in browsers. Webpack supports modules written in a variety of languages and preprocessors, via loaders. Loaders describe to webpack how to process non-JavaScript modules and include these dependencies into your bundles.
There are a few concepts to know about how webpack works
- Entry - module to begin dependency graph (default: ./src/index.js)
- Output - where to emit bundles (default: ./dist/main.js)
- Loaders - default only JavaScript and JSON files. allow webpack to process, convert other files into valid modules
- Plugins - wider range of tasks like bundle optimization, asset management and injection of environment variables
- Mode - set to: development, production or none; enables built-in optimizations (default: production)
- Browser Compatibility - supports all browsers that are ES5-compliant
Simple usage
i. Install
#start project
npm init
#for development only
npm install webpack --save-dev
#instead of using `file:///` url
npm install webpack-dev-server --save-dev
ii. Configure
Used like: webpack <entry_point> <output_file>
//package.json
"scripts":{
"build": "webpack-dev-server --entry ./src/js/app.js --output-filename ./dist/bundle.js"
"build-prod": "webpack /src/js/app.js dist/bundle.js -p" #minify for production
}
iii. Implement
What dependencies are determined by these only es6 syntax.
//app.js
import {name} from './domloader.js'
//domloader.js
export var name = <function>
//index.html
<script src="dist/bundle.js">
Simple configuration
//package.json
"scripts": {
"build": "webpack --config prod.config.js"
}
//webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
Development
Plugins can support your workflow. These are three useful plugins:
- HtmlWebpackPlugin - generates new names in output
- clean-webpack-plugin - clean /dist folder before each output
Automatically update webpage
npm start
//package.json
{
"name": "development",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"start": "webpack-dev-server --open",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
//webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
app: './src/index.js',
print: './src/print.js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
},
plugins: [
// new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Development'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
Advanced Topics
Vue Ecosystem
Tooling
- Vue CLI - interactive project scaffolding with zero config rapid prototyping, a runtime dependency (@vue/cli-service) and a full graphical user interface to create and manage Vue.js projects
- vue-cli-service - built on top of webpack that loads other plugins
- @vue/cli-plugin- (for built-in plugins)
- vue-cli-plugin- (for community plugins)
- Vue (Webpack) Loader - allows you to author Vue components in a format called Single-File Components (SFCs)
- Vue (browser) dev-tools Extension - for in-browser development
Libraries
- Testing: Vue-Test-Utils
- HTTP Client: Axios - requests for consuming API
- Router - official router support
- State Management: Vuex - an architecture for Vue applications and allows simple management of the application state.
- Pre-Render, Production: NuxtJs - opinionated for spa
Component Search
Dev Env
Vue CLI Scaffolding
Projects created by Vue CLI are pre-configured with most of the common development needs working out of the box, including Vue Loader.
#single component
npm install -g @vue/cli-service-global
vue serve
(browser)localhost:8080
#project
npm install -g @vue/cli
vue --version
vue create my-project
npm run serve
(browser)localhost:8080
#or with ui for dependency management
vue ui
#or directly access the binary
npx vue-cli-service serve
Project Structure
- node_modules folder contains the packages that the app and development tools require.
- public folder contains static project assets, which will not be included in the bundling process.
- src folder contains the Vue.js application with all resources.
- assets folder is used for static resources required by the app, which will be included in the bundling process.
- components folder is used for the application’s components.
- views folder is used for components which will be displayed using the URL routing feature.
- App.vue is the root component.
- main.js is the JavaScript file that creates the Vue instance object.
- router.js is used to configure the Vue router.
- store.js is used to configure the data store created with Vuex.
- .gitignore contains a list of files and folders that are excluded from the Git version control.
- babel.config.js contains the configuration settings for the Babel compiler.
- package.json contains a list of the packages required for Vue.js development as well as the commands used for the development tools.
- package-lock.json contains a complete list of the packages required by the project and their dependencies.
- README.md contains general information about the project.
#install plugin
vue add bootstrap-vue
src/plugins/<new_plugin>
#build production bundle of: app, library, or component
npm run build --modern --target app
dist
folder created and two version with the browser-appropriate version selected automatically
#analyze the build
npm run inspect
TODO: Guide > Development
Vue Basics
Vue.js allows you to simply bind your data models to the representation layer. It also allows you to easily reuse components throughout the application.
Useful Frontend Terms
- Declarative Views: These are the Views that provide a way of direct data binding between plain JavaScript data models and the representation.
- Directives: These are special HTML elements attributes prefixed with
v-
in Vue.js that allow data binding in different ways. They apply special reactive behavior to the rendered DOM. - Reactivity: In the Web, this is actually the immediate propagation of any changes of data to the View layer.
Vue Foundations
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
Directives
vue directive syntax:
<div v-directive:argument of [dynamic argument].<event_modifier or key_modifier>=" js expression | filter ">
* data output
- {{}}
<div>{{ interpolated_data }}</div>
- v-bind <div v-bind:id="dynamicId"></div> or ':id'
:[attribName / null]="var"
:id="elemId"
:disabled="null"
:href="url"
- v-once
- v-html
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
* data input
- v-model
* conditionals
- v-if
- v-else-if
- v-else
- v-show
- v-for="(item, index) in items">
v-for="(value, name, index) in object">
v-for="todo in todos" v-if="!todo.isComplete">
* event listener
- v-on or '@click'
:[eventName]
:keyup.enter
:keyup.page-down
:click
:click="say('what')">
:click.once
:hover
:focus
:submit
* style
- v-bind:class="{ active: isActive }"></div>
- v-bind:style="[base, { color: activeColor, fontSize: fontSize + 'px' }]"></div>
Data
instance properties (prefixed by $
) are different from user-defined (var data={} ) data
Implementation designs
All js in HTML
<script>
var vm = new Vue({
el: '#example',
data: {},
props: ['item'],
methods: {
name: function(){
return this.msg
},
computed:
<hooks:created, mounted, updated, destroyed>: function(){}
})
</script>
Split js into a globally-registered component
<div id='timer'>
<Timer title='my timer'></Timer>
</div>
Vue.component('Timer',{
template: '<div>...</div>',
props: ['title'], //data passed to and maintained by instance
data: function () { //data must be a function to maintain indep data copy
return {count: 0}
},
methods:
})
new Vue({
el: "#timer",
});
Use local registration of sub-component
//ComponentA.vue
//ComponentB.vue
import ComponentA from './ComponentA'
export default {
components: {
ComponentA
}
}
//main.js
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
//index.html
Single-file component separation-of-layers
//comp.vue
<template>
<div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>
Typical single-file component
This is advanced javascript and must understand
#index.html
<div id="app"></div>
//index.js
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
template: '<App/>',
components: { App }
})
//App.vue
<template>
<div id="app">
<h1>My Todo App!</h1>
<TodoList/>
</div>
</template
<script>
import TodoList from './components/TodoList.vue'
export default {
components: {
TodoList
}
}
</script>
<style></style>
Unit-testing individual components
//Component.vue
<template>
<p>{{ msg }}</p>
</template>
<script>
export default {
props: ['msg']
}
</script>
//test.js
import Vue from 'vue'
import MyComponent from './MyComponent.vue'
// helper function that mounts and returns the rendered text
function getRenderedText (Component, propsData) {
const Constructor = Vue.extend(Component)
const vm = new Constructor({ propsData: propsData }).$mount()
return vm.$el.textContent
}
describe('MyComponent', () => {
it('renders correctly with different props', () => {
expect(getRenderedText(MyComponent, {
msg: 'Hello'
})).toBe('Hello')
expect(getRenderedText(MyComponent, {
msg: 'Bye'
})).toBe('Bye')
})
})
TODO
END