Local Server Setup: Building a Local Environment for Frontend Development â
Why Need a Local Server? â
Modern frontend development has gone beyond simple static web page development. We need local servers to simulate real network environments and provide a complete development experience. A local server is not only an indispensable tool in the development process but also the infrastructure for the separation of frontend and backend architecture.
Core Value of Local Server â
Simulate Real Environment: Local servers can simulate the HTTP request and response mechanisms of the production environment, ensuring that the code works properly after being deployed to the server.
Hot Reload Function: Modern frontend development servers support hot reload, automatically refreshing the page after code modification, greatly improving development efficiency.
Solve Cross-Origin Issues: Through proxy configuration, cross-origin request issues during development can be easily solved without complex configurations.
Modular Support: Modern frontend servers support ES6 modules, TypeScript, etc., allowing direct use of modern JavaScript features without manual compilation.
Development Convenience: Provide features needed during development such as development tool integration, error prompts, and performance monitoring.
Different Types of Local Servers â
Static File Server: Used to host static files such as HTML, CSS, JavaScript, suitable for simple website development and prototyping.
Development Server: Provides advanced features such as hot reload, module resolution, proxy forwarding, suitable for modern frontend framework development.
API Server: Provides backend services such as RESTful API, GraphQL, supporting data interaction and business logic.
Full Stack Server: Provides both frontend resources and backend API services, suitable for full-stack development projects.
Static File Server Setup â
Python SimpleHTTPServer â
Python's built-in HTTP server is the simplest static file server solution:
# Python 3.x
python -m http.server 8000
# Python 2.x
python -m SimpleHTTPServer 8000
# Specify port and directory
python -m http.server 8080 --directory /path/to/your/projectUsage Example:
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Local Server Test</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container">
<h1>Local Server Test Page</h1>
<p>Current Time: <span id="current-time"></span></p>
<button id="fetch-btn">Fetch API Data</button>
<div id="result"></div>
</div>
<script src="script.js"></script>
</body>
</html>// script.js
function updateTime() {
const now = new Date();
document.getElementById("current-time").textContent = now.toLocaleString();
}
async function fetchData() {
try {
const response = await fetch("/api/test.json");
const data = await response.json();
document.getElementById("result").textContent = JSON.stringify(data);
} catch (error) {
document.getElementById("result").textContent =
"Failed to fetch data: " + error.message;
}
}
// Initialize
updateTime();
setInterval(updateTime, 1000);
document.getElementById("fetch-btn").addEventListener("click", fetchData);// api/test.json
{
"message": "Hello from local server!",
"timestamp": "2023-11-27T10:00:00Z",
"status": "success"
}Node.js http-server â
Node.js http-server is a more feature-rich static file server:
# Global install
npm install -g http-server
# Start server
http-server [path] [options]
# Common options
http-server -p 8080 -o -c-1 -S -C cert.pem -K key.pemConfiguration Options:
-p or --port # Specify port (default 8080)
-a or --address # Specify address (default 0.0.0.0)
-d or --directory # Specify directory (default current directory)
-o or --open # Open browser automatically after start
-c or --cache # Set cache time (default 3600 seconds, -1 to disable cache)
-s or --silent # Silent mode
-S or --ssl # Enable HTTPS
-C or --cert # SSL certificate file
-K or --key # SSL private key fileConfiguration File: http-server.json
{
"port": 8080,
"host": "localhost",
"root": "./public",
"open": true,
"cache": -1,
"ext": true,
"cors": true,
"gzip": true
}Live Server (VS Code Extension) â
Live Server is the most popular local server extension in VS Code:
Installation and Configuration:
- Search for "Live Server" in VS Code Extension Marketplace.
- Install the Live Server extension developed by Ritwick Dey.
- Right-click HTML file and select "Open with Live Server".
Custom Configuration:
// settings.json
{
"liveServer.settings.port": 5500,
"liveServer.settings.root": "/",
"liveServer.settings.NoBrowser": false,
"liveServer.settings.host": "localhost",
"liveServer.settings.fullReload": false,
"liveServer.settings.wait": 1000,
"liveServer.settings.customBrowser": "chrome",
"liveServer.settings.ChromeDebuggingAttachment": false,
"liveServer.settings.ignoreFiles": [".vscode/**", "**/*.scss", "**/*.sass"],
"liveServer.settings.https": {
"enable": false,
"cert": "/path/to/cert.pem",
"key": "/path/to/key.pem",
"passphrase": ""
}
}Advanced Features:
<!-- Configure Live Server options in HTML -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Live Server Test</title>
<!-- Live Server Control -->
<script>
// Disable auto reload
window.liveServer && window.liveServer.disconnect();
// Manually trigger reload
function forceReload() {
window.liveServer && window.liveServer.refresh();
}
</script>
</head>
<body>
<button onclick="forceReload()">Force Reload</button>
<div id="app"></div>
</body>
</html>Modern Development Servers â
Webpack Dev Server â
Webpack Dev Server is the most professional development server, perfectly integrated with modern frontend toolchains:
Basic Configuration: webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.[hash].js",
clean: true,
},
devServer: {
static: {
directory: path.join(__dirname, "public"),
},
compress: true,
port: 9000,
open: true,
hot: true,
liveReload: true,
historyApiFallback: true,
// Proxy Configuration
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
pathRewrite: {
"^/api": "",
},
},
// Multiple Proxies
"/auth": {
target: "http://localhost:4000",
changeOrigin: true,
secure: false,
},
},
// HTTPS Configuration
https: {
key: fs.readFileSync("./server.key"),
cert: fs.readFileSync("./server.crt"),
},
// Custom Middleware
setupMiddlewares: (middlewares, devServer) => {
if (!devServer) {
throw new Error("webpack-dev-server is not defined");
}
// Custom API Endpoint
devServer.app.get("/api/test", (req, res) => {
res.json({ message: "Hello from webpack dev server!" });
});
return middlewares;
},
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
};Start Command:
// package.json
{
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production",
"serve": "webpack serve --open --mode development",
"dev": "webpack serve --compress --port 3000 --open"
}
}Hot Module Replacement (HMR) Configuration:
// src/index.js
import "./styles.css";
import { createApp } from "./app";
const app = createApp();
// Enable HMR
if (module.hot) {
module.hot.accept("./app.js", () => {
const nextApp = require("./app.js").createApp();
app.destroy();
nextApp.mount();
});
module.hot.accept("./styles.css");
}
app.mount();
// Destroy method
app.destroy = function () {
console.log("App instance destroyed");
};Vite Dev Server â
Vite is the next-generation frontend build tool, providing extremely fast development experience:
Basic Configuration: vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { resolve } from "path";
export default defineConfig({
plugins: [react()],
// Development Server Configuration
server: {
host: "localhost",
port: 3000,
open: true,
cors: true,
// Proxy Configuration
proxy: {
"/api": {
target: "http://backend-server:3001",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
// WebSocket Proxy
"/socket.io": {
target: "ws://localhost:4000",
ws: true,
},
},
},
// Preview Server Configuration (Production Preview)
preview: {
port: 4173,
host: "localhost",
https: true,
},
// Build Optimization
build: {
outDir: "dist",
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
utils: ["lodash", "axios"],
},
},
},
},
// Path Alias
resolve: {
alias: {
"@": resolve(__dirname, "src"),
"@components": resolve(__dirname, "src/components"),
"@utils": resolve(__dirname, "src/utils"),
},
},
// CSS Configuration
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
},
});Project Initialization:
# Create Vite Project
npm create vite@latest my-vite-app -- --template react
cd my-vite-app
# Install Dependencies
npm install
# Start Development Server
npm run dev
# Build for Production
npm run build
# Preview Production Build
npm run previewPlugin Configuration Example:
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { VitePWA } from "vite-plugin-pwa";
import { crxHmr } from "@crxjs/vite-plugin";
import svgr from "vite-plugin-svgr";
export default defineConfig({
plugins: [
react(),
// SVG Component Support
svgr({
svgrOptions: {
icon: true,
svgo: false,
},
}),
// PWA Support
VitePWA({
registerType: "autoUpdate",
workbox: {
globPatterns: ["**/*.{js,css,html,ico,png,svg}"],
},
}),
],
// Environment Variable Configuration
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
},
});Framework Specific Development Servers â
Create React App â
Development server integrated in React official scaffold:
// Custom Start Configuration: .env.development
REACT_APP_API_URL=http://localhost:4000
REACT_APP_ENV=development
GENERATE_SOURCEMAP=true
// .env.production
REACT_APP_API_URL=https://api.production.com
REACT_APP_ENV=production
GENERATE_SOURCEMAP=falseCustom Webpack Configuration: config-overrides.js
const {
override,
addWebpackResolve,
addBabelPlugin,
} = require("customize-cra");
module.exports = override(
// Add Path Alias
addWebpackResolve({
extensions: [".ts", ".tsx", ".js", ".jsx"],
alias: {
"@": path.resolve(__dirname, "src"),
},
}),
// Add Babel Plugin
addBabelPlugin([
"import",
{
libraryName: "antd",
libraryDirectory: "es",
style: "css",
},
])
);Vue CLI Development Server â
Vue CLI provides rich development server configurations:
// vue.config.js
const path = require("path");
module.exports = {
devServer: {
port: 8080,
host: "localhost",
open: true,
https: false,
// Proxy Configuration
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
ws: true,
pathRewrite: {
"^/api": "",
},
},
},
// Before Start Configuration
before: function (app) {
app.get("/api/mock", (req, res) => {
res.json({
message: "Mock data from Vue dev server",
timestamp: Date.now(),
});
});
},
},
// Path Alias
configureWebpack: {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
"@components": path.resolve(__dirname, "src/components"),
},
},
},
};Angular Development Server â
Angular CLI's development server supports powerful configuration options:
// angular.json
{
"projects": {
"my-app": {
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"ssl": false,
"sslKey": "./server.key",
"sslCert": "./server.crt",
"port": 4200,
"host": "localhost",
"proxyConfig": "proxy.conf.json"
}
}
}
}
}
}Proxy Configuration: proxy.conf.json
{
"/api/*": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
},
"/api/auth/*": {
"target": "http://auth-server:4000",
"secure": false,
"changeOrigin": true
}
}Dockerized Development Server â
Basic Docker Configuration â
Dockerfile:
FROM node:16-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Expose port
EXPOSE 3000
# Start command
CMD ["npm", "start"]docker-compose.yml:
version: "3.8"
services:
frontend:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- API_URL=http://backend:8000
command: npm run dev
backend:
image: node:16-alpine
working_dir: /app
volumes:
- ./backend:/app
ports:
- "8000:8000"
command: npm run dev
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:password@db:5432/mydb
db:
image: postgres:13
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:Development Environment Optimization â
Multi-stage Build Dockerfile:
# Development Stage
FROM node:16-alpine as development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# Production Stage
FROM node:16-alpine as production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=development /app/dist ./dist
COPY --from=development /app/public ./public
EXPOSE 80
CMD ["node", "dist/server.js"]HTTPS Local Server â
Self-signed Certificate Generation â
Generate Certificate using OpenSSL:
# Generate private key
openssl genrsa -out server.key 2048
# Generate certificate signing request
openssl req -new -key server.key -out server.csr
# Generate self-signed certificate
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
# Generate certificate in one go
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodesNode.js HTTPS Server â
Basic HTTPS Server:
// https-server.js
const https = require("https");
const fs = require("fs");
const path = require("path");
const options = {
key: fs.readFileSync(path.join(__dirname, "server.key")),
cert: fs.readFileSync(path.join(__dirname, "server.crt")),
};
const server = https.createServer(options, (req, res) => {
// Set CORS headers
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
// Route handling
if (req.url === "/" || req.url === "/index.html") {
fs.readFile(path.join(__dirname, "public", "index.html"), (err, data) => {
if (err) {
res.writeHead(500);
return res.end("Server Error");
}
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
} else if (req.url.endsWith(".js")) {
fs.readFile(path.join(__dirname, "public", req.url), (err, data) => {
if (err) {
res.writeHead(404);
return res.end("File Not Found");
}
res.writeHead(200, { "Content-Type": "application/javascript" });
res.end(data);
});
} else {
res.writeHead(404);
res.end("Not Found");
}
});
const PORT = 4430;
server.listen(PORT, () => {
console.log(`HTTPS server running on port ${PORT}`);
});Express HTTPS Configuration â
// express-https.js
const express = require("express");
const https = require("https");
const fs = require("fs");
const path = require("path");
const cors = require("cors");
const morgan = require("morgan");
const app = express();
// Middleware configuration
app.use(cors());
app.use(morgan("combined"));
app.use(express.json());
app.use(express.static("public"));
// API Routes
app.get("/api/data", (req, res) => {
res.json({
message: "Hello from HTTPS server",
timestamp: new Date().toISOString(),
secure: true,
});
});
// Proxy Middleware
app.use(
"/api",
createProxyMiddleware({
target: "http://localhost:3001",
changeOrigin: true,
pathRewrite: {
"^/api": "",
},
})
);
const options = {
key: fs.readFileSync(path.join(__dirname, "server.key")),
cert: fs.readFileSync(path.join(__dirname, "server.crt")),
};
const server = https.createServer(options, app);
const PORT = process.env.HTTPS_PORT || 4430;
server.listen(PORT, () => {
console.log(`Express HTTPS server running on port ${PORT}`);
});Local Certificate Trust Settings â
Windows System:
# Import certificate to Trusted Root Certification Authorities
# 1. Double click cert.crt file
# 2. Select "Install Certificate"
# 3. Select "Current User" or "Local Machine"
# 4. Select "Trusted Root Certification Authorities"
# 5. Complete importmacOS System:
# Add certificate to Keychain
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain server.crt
# Or use Keychain Access app to add manuallyAdvanced Configuration and Optimization â
Multi-environment Configuration â
Environment Variable Management:
// server.js
const express = require("express");
const path = require("path");
const config = {
development: {
port: 3000,
apiBaseUrl: "http://localhost:4000",
enableCors: true,
enableLogging: true,
},
staging: {
port: 3001,
apiBaseUrl: "https://api-staging.example.com",
enableCors: true,
enableLogging: true,
},
production: {
port: 80,
apiBaseUrl: "https://api.example.com",
enableCors: false,
enableLogging: false,
},
};
const env = process.env.NODE_ENV || "development";
const appConfig = config[env];
const app = express();
// Configure middleware based on environment
if (appConfig.enableCors) {
app.use(cors());
}
if (appConfig.enableLogging) {
app.use(morgan("combined"));
}
// Expose config to frontend
app.get("/config", (req, res) => {
res.json({
apiBaseUrl: appConfig.apiBaseUrl,
environment: env,
});
});
app.listen(appConfig.port, () => {
console.log(`Server running in ${env} mode on port ${appConfig.port}`);
});Environment Variable Files:
# .env.development
NODE_ENV=development
PORT=3000
API_URL=http://localhost:4000
LOG_LEVEL=debug
# .env.production
NODE_ENV=production
PORT=80
API_URL=https://api.example.com
LOG_LEVEL=error
# .env.staging
NODE_ENV=staging
PORT=3001
API_URL=https://api-staging.example.com
LOG_LEVEL=infoPerformance Optimization â
Gzip Compression Configuration:
const compression = require("compression");
app.use(
compression({
filter: (req, res) => {
if (req.headers["x-no-compression"]) {
return false;
}
return compression.filter(req, res);
},
level: 6,
threshold: 1024,
})
);Static Resource Caching:
const express = require("express");
const app = express();
// Static resource configuration
app.use(
express.static("public", {
maxAge: "1y", // Cache for 1 year
etag: true,
lastModified: true,
setHeaders: (res, path) => {
if (path.endsWith(".html")) {
res.setHeader("Cache-Control", "no-cache");
}
},
})
);Memory Usage Optimization:
// Enable gzip compression
app.use(compression());
// Limit request body size
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
// Connection pool configuration
const pool = mysql.createPool({
connectionLimit: 10,
host: "localhost",
user: "root",
password: "password",
database: "mydb",
});Monitoring and Logging â
Health Check Endpoint:
app.get("/health", (req, res) => {
const health = {
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.version,
};
res.json(health);
});
app.get("/metrics", (req, res) => {
const metrics = {
memory: process.memoryUsage(),
cpu: process.cpuUsage(),
uptime: process.uptime(),
activeConnections: server.connections,
};
res.json(metrics);
});Logging System Configuration:
const winston = require("winston");
const logger = winston.createLogger({
level: "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: "frontend-server" },
transports: [
new winston.transports.File({ filename: "error.log", level: "error" }),
new winston.transports.File({ filename: "combined.log" }),
],
});
if (process.env.NODE_ENV !== "production") {
logger.add(
new winston.transports.Console({
format: winston.format.simple(),
})
);
}Common Issues and Solutions â
Port Occupied Issue â
# Find process occupying port
# Windows
netstat -ano | findstr :3000
# macOS/Linux
lsof -i :3000
# Force kill process
# Windows
taskkill /PID <PID> /F
# macOS/Linux
kill -9 <PID>CORS Error Resolution â
// Complete CORS configuration
app.use(
cors({
origin: function (origin, callback) {
const allowedOrigins = ["http://localhost:3000", "http://127.0.0.1:3000"];
if (!origin || allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
})
);Hot Reload Not Working â
// File watch configuration
module.exports = {
devServer: {
watchFiles: ["src/**/*", "public/**/*"],
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1000,
},
},
};Summary â
Local servers are the infrastructure of frontend development. Mastering different types of server configurations and optimization techniques can significantly improve development efficiency and application quality.
Key Points Review:
- Local servers simulate real network environments, providing hot reload, proxy, and other functions.
- Static file servers are suitable for simple website development.
- Modern development servers support advanced features like module resolution and hot module replacement.
- Framework-specific development servers provide optimized development experiences.
- Dockerized development servers ensure environment consistency and portability.
- HTTPS configuration makes the development environment closer to production.
- Multi-environment configuration supports different needs for development, testing, and production.
- Performance optimization and monitoring ensure server stability and efficiency.
Choosing the right local server solution should be based on project needs, team size, and technology stack. Simple projects may only need a basic static file server, while complex enterprise applications require fully functional development servers.