Knockout.js:如何封装ViewModel并实现对外隐藏?
Great question—exposing your view models directly on window creates unnecessary risk of unintended external calls or modifications. Let’s fix this while keeping the flexibility to reuse view models across your entire page.
Solution 1: Use an Immediately Invoked Function Expression (IIFE) for Private Scope
The simplest way to hide your view models from the global scope is to wrap all your Knockout initialization code in an IIFE. This creates a private closure where your view models live, never exposing them to window.
Code Implementation
// Wrap everything in an IIFE to create a private scope (function() { // Define your view models inside the closure—they're not accessible outside const vmA = new VmA(); const vmB = new VmB(); const vmC = new VmC(); // Create your master view model (still private) const masterVM = { vmA, vmB, vmC }; // Apply bindings to the root element, using masterVM as the binding context ko.applyBindings(masterVM, document.documentElement); // Optional: Add internal initialization logic here (e.g., vmA.loadData()) })();
Update Your HTML Bindings
Since masterVM is now the root binding context, you can simplify your data-bind attributes—no need to reference masterVM explicitly:
<div data-bind="click: vmA.demo">Click</div> <div data-bind="click: vmC.demo">Click</div>
Solution 2: Use ES Modules (For Modern Projects)
If you’re using a modern build setup (like Webpack, Vite, or even native ES modules in the browser), you can encapsulate your view models in a module. Modules inherently keep variables private unless explicitly exported.
Step 1: Create a View Model Module
// viewModels.js export function initViewModels() { const vmA = new VmA(); const vmB = new VmB(); const vmC = new VmC(); const masterVM = { vmA, vmB, vmC }; ko.applyBindings(masterVM, document.documentElement); // Optional: Export only specific methods if you need external access (use sparingly!) return { triggerVmADemo: () => vmA.demo() }; }
Step 2: Initialize in Your Entry Script
// main.js import { initViewModels } from './viewModels.js'; initViewModels();
Optional: Safe Limited Exposure
If you absolutely need to trigger some view model logic from outside the closure/module (e.g., a global event listener), avoid exposing the entire view model. Instead, expose only specific, controlled methods:
// Inside the IIFE or module window.safePublicApi = { runVmADemo: () => vmA.demo(), refreshVmCData: () => vmC.fetchData() };
This way, external code can only execute the actions you explicitly allow, not modify the view model directly.
内容的提问来源于stack exchange,提问作者Alvaro

