使用Webpack+原生JavaScript操作DOM遇报错及架构疑问
register is not defined Error + Implementing MVC with Webpack & Vanilla JS Hey there! Let's tackle your issues one by one—first the error you're seeing, then how to implement a clean MVC-style architecture with your Webpack setup.
1. Fixing the Uncaught ReferenceError: register is not defined
The root cause here is Webpack's module scoping. By default, Webpack wraps your code in an isolated module context, so functions like register() don't get attached to the global window object. Your inline onclick="register()" tries to call a global function that doesn't exist.
Here are two solutions, with the second being more aligned with modern modular practices:
Option 1: Expose the function to the global scope
Modify your src/index.js to explicitly attach the function to window:
function register() { alert('Hello') } // Expose to global scope so inline onclick can find it window.register = register;
Option 2: Use addEventListener instead of inline handlers (Recommended)
Inline event handlers mix HTML and JS, which makes code harder to maintain. Instead, bind the event directly in your JS:
First, update your HTML to remove the inline onclick:
<div class="form-group"> <label for="matri">Matri</label> <input class="form-control col-12" type="text" placeholder="Enter your matri" id="matry"> </div> <div class="text-left mt-5" style="width: 100%;"> <!-- Remove onclick attribute --> <button class="btn btn-primary" id="registerBtn" type="button">Register</button> </div>
Then update src/index.js to bind the event:
function register() { alert('Hello') } // Wait for DOM to load before accessing elements document.addEventListener('DOMContentLoaded', () => { const registerBtn = document.getElementById('registerBtn'); registerBtn.addEventListener('click', register); });
2. Implementing MVC Architecture (Absolutely Feasible & Recommended)
Using MVC with vanilla JS and Webpack is a great choice—it will make your code more organized, testable, and scalable. Webpack's modular system fits perfectly with MVC's separation of concerns.
Recommended Project Structure
Let's adjust your existing structure to follow MVC principles:
your-project/ ├── src/ │ ├── models/ # Data & business logic │ │ └── UserModel.js │ ├── controllers/ # Mediates between Model and View │ │ └── AuthController.js │ ├── views/ # DOM interactions & UI rendering │ │ └── AuthView.js │ ├── styles/ # SCSS/CSS files │ └── index.js # Entry point ├── dist/ ├── index.html └── webpack.config.js
Example Code for Each Layer
Model Layer (src/models/UserModel.js)
Handles data validation, API calls, and data storage—no DOM interaction here:
export default class UserModel { // Validate registration input static validateMatri(matri) { if (!matri || matri.length < 3) { return 'Matri must be at least 3 characters long'; } return null; // No error } // Simulate API registration call static async registerUser(matri) { // Replace with real API fetch logic return new Promise((resolve) => { setTimeout(() => { resolve({ success: true, message: 'Registration successful!' }); }, 1000); }); } }
View Layer (src/views/AuthView.js)
Handles all DOM interactions—renders UI, listens for user events, and updates the interface:
export default class AuthView { constructor() { this.registerBtn = document.getElementById('registerBtn'); this.matriInput = document.getElementById('matry'); this.errorElement = document.createElement('div'); this.errorElement.className = 'text-danger mt-2'; this.matriInput.parentNode.appendChild(this.errorElement); } // Get input value getMatriValue() { return this.matriInput.value.trim(); } // Show error message showError(message) { this.errorElement.textContent = message; } // Clear error message clearError() { this.errorElement.textContent = ''; } // Bind registration button click event bindRegister(handler) { this.registerBtn.addEventListener('click', handler); } // Show success message showSuccess(message) { alert(message); // Or replace with a nicer UI component } }
Controller Layer (src/controllers/AuthController.js)
Acts as the middleman—connects the View and Model, handles business logic:
import UserModel from '../models/UserModel.js'; import AuthView from '../views/AuthView.js'; export default class AuthController { constructor() { this.view = new AuthView(); this.model = UserModel; // Bind the register handler to the view this.view.bindRegister(this.handleRegister.bind(this)); } async handleRegister() { this.view.clearError(); const matri = this.view.getMatriValue(); // Validate input via model const validationError = this.model.validateMatri(matri); if (validationError) { this.view.showError(validationError); return; } // Try to register via model try { const result = await this.model.registerUser(matri); this.view.showSuccess(result.message); this.matriInput.value = ''; // Clear input } catch (error) { this.view.showError('Registration failed. Please try again.'); } } }
Entry Point (src/index.js)
Initialize the controller to kick off the app:
import AuthController from './controllers/AuthController.js'; import './styles/main.scss'; // Import your styles here // Wait for DOM to be fully loaded before initializing document.addEventListener('DOMContentLoaded', () => { new AuthController(); });
3. Minor Webpack Config Tweaks (Optional)
- In Webpack 4+, when
mode: 'production', theUglifyJSPluginis included by default—you can remove it from your plugins list to avoid redundancy. - Your
output.filenamecan be simplified tobundle.jssincepathalready points todist:output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }
内容的提问来源于stack exchange,提问作者Thiago Cunha

