Scaffolding, Project Initialization, and Component Development
EJS Template Engine: Three Usage Patterns
The EJS (Embedded JavaScript) template engine provides flexible ways to generate HTML. Here are three fundamental approaches:
// index.js
const ejs = require('ejs');
const path = require('path');
const fs = require('fs');
const templateString = '<div><%= userData.name %></div>';
const config = {};
const userData1 = { name: "alice" };
const userData2 = { name: "bob" };
// 1. Compile and reuse
const compiledFn = ejs.compile(templateString, config);
const output1 = compiledFn(userData1);
const output2 = compiledFn(userData2);
// 2. Direct rendering
const directOutput = ejs.render(templateString, userData1, config);
// 3. Render from file
// 3.1 Promise-based
ejs.renderFile(path.resolve(__dirname, 'template.html'), userData1, config)
.then(renderedContent => console.log(renderedContent));
// 3.2 Callback-based with custom options
const callbackData = {
user: { name: "charlie", nickname: "<div>charlie</div>", copyright: "example" }
};
const callbackConfig = { delimiter: "%" };
// Custom file loader
ejs.fileLoader = function(filePath) {
const originalContent = fs.readFileSync(filePath).toString();
return '<div style="color: blue;">Custom Footer: <%= user.copyright %></div>' + originalContent;
};
ejs.renderFile(path.resolve(__dirname, 'template.html'), callbackData, callbackConfig, (err, result) => {
if (err) console.error(err);
console.log(result);
});
EJS Tags:
<%: Script tag for control flow, no output.<%_: Trims preceding whitespace.<%=: Outputs escaped HTML.<%-: Outputs unescaped data.<%#: Comment tag, ignored.<%%: Outputs literal '<%'.%>: Standard end tag.-%>: Trims following newline._%>: Trims following whitespace.
// template.html
<% if(userData) { %>
<% for(let i = 0; i < 5; i++) { %>
<!-- Display user name -->
<div><%= userData.name %></div>
<%- userData.nickname %>
<% } %>
<% } %>
<!-- Include footer -->
<%- include('./footer.html', { user: userData }) %>
// footer.html
<div>Footer: <%= user.copyright %></div><% -%>
Glob Module: File Pattern Matching
The glob module simplifies finding files matching patterns.
// Install: npm i glob@8
const glob = require('glob');
glob('**/*.js', {
ignore: ['node_modules/**', 'webpack.config.js']
}, (error, matchedFiles) => {
if (error) console.error(error);
console.log('Matched files:', matchedFiles);
});
EJS Source Code Deep Dive: Template Rendering Mechanism
EJS's core execution flow:
- new Template: Initializes the template object.
- compile: Compiles the template into a function that accepts data for rendering.
Compilation Steps:
template → createRegex: Generates regex for template parsing.generateSource → generateFunctionSource → generateFunction: Produces the executable function.
require Module Loading: Understanding Node.js Module System
Usage Scenarios:
- Load built-in modules:
require('fs') - Load npm modules:
require('ejs') - Load local modules:
require('./utils')
Supported File Types:
- .js, .json, .node, .mjs, and other types (treated as JS)
Key Questions:
- How does CommonJS load main modules?
- How are built-in modules loaded?
- How are
node_modulesmodules resolved? - Why are non-JS/JSON/node files treated as JS?
- How is module caching handled?
Module Object Properties:
id: Full file pathpath: Directory of the fileexports: Module's exported contentparent: Parent modulefilename: File pathloaded: Loading statuschildren: Child modulespaths: Module lookup paths
Execution Flow:
Module._loadloadNativeModule: For built-in modulesnew Module: Instantiate moduleModule._cache[filename] = module: Cache module
module.load(filename)findLongestRegisteredExtension: Determine file extensionModule._extensions[extension]: Execute file
module._compilecompileFunction: Generate executable functioncompiledWrapper.call: Execute module code
B2B Project Analysis and Architecture Design
Introduction
This section covers:
- Project types for overcoming development plateaus
- Identifying key challenges from requirements
- Creating technical solutions
- Technology selection
Key Concepts:
- Identifying pain points
- Documenting technical solutions
- Reusable business component libraries
- Editor design (mapping UI to data)
- TypeScript, Vue 3, React
Complex Projects
Business Complexity:
- Interaction complexity
- Data structure and state complexity
- Inter-project dependencies
- Packaging and performance optimization
- Third-party integration
Process Complexity:
- Git workflow
- Linting tools
- Unit testing
- Commit messages
- PR reviews
- CI/CD
Requirement Analysis
Project Challenges:
- Component implementation
- Cross-project reuse
- Component extensibility
- Editor state management
- Add/remove operations
- Property-to-form rendering
- Real-time feedback
- Plugin architecture
Component Library Solutions:
- Sharing components across projects
- Property design strategies
- Maintaining extensibility
Editer Implementation (Pseudo-code)
Store Structure:
interface EditorStore {
components: ComponentData[];
currentElement: string;
}
interface ComponentData {
props: { [key: string]: any };
id: string;
name: string;
}
const components = [
{ id: '1', type: 'l-text', props: { text: 'hello', color: 'green' }},
{ id: '2', type: 'l-text', props: { text: 'world', color: 'purple' }}
];
// Render components
components.map(component => <component.name { ...component.props } />);
const templateComponents = [
{ type: 'l-text', props: { text: 'Template 1', color: 'green' }},
{ type: 'l-text', props: { text: 'Template 2', color: 'purple' }}
];
// Render with wrapper
templateComponents.map(component => <Wrapper><component.name { ...component.props } /></Wrapper>);
// Remove component
components = components.filter(c => c.id !== '1');
Selection Handling:
const textComponentProps = {
text: 'hello',
fontFamily: 'Arial',
color: '#000'
};
const propsMap = {
text: { component: 'input' },
fontFamily: { component: 'dropdown' },
color: { component: 'color-picker' }
};
Object.entries(textComponentProps).map(([key, value]) => {
const handleChange = (propKey: string, newValue: any, id: string) => {
const component = store.components.find(c => c.id === id);
if (component) component.props[propKey] = newValue;
};
return <propsMap[key].component value={value} onChange={(newValue) => handleChange(key, newValue, '1')} />;
});
Technology Selection
- TypeScript: Improved code understanding, higher efficiency, fewer errors, good TypeScript compatibility.
- Vue vs React: Implementation style, data update mechanisms, code reusability.
Language (TypeScript) and Framework (Vue 3)
- Scaffolding (linding-cli-dev)
- Testing (jest + vue-test-utils)
- Build tools (webpack+rollup)
- CI/CD (travis)
- UI library (ant-design-vue)
- State management and routing (vuex, vue-router)
- Third-party libraries
- Styling solutions
Frontend Fundamentals Review
TypeScript
Key Concepts:
- Basic types
- Interfaces
- Classes
- Ganerics
- Declaration files
- Type inference
- Union types
- Intersection types
- Type assertions
- Built-in types
- Type aliases
- Keyof and in operators
// Function with properties
interface FunctionWithProps {
(x: number): number;
name: string;
}
const fn: FunctionWithProps = (x: number) => x;
fn.name = 'example';
// Interface for classes
interface ClockInterface {
currentTime: number;
alert(t: number): void;
}
interface ClockConstructor {
new (h: number, m: number): void;
time: number;
}
const Clock: ClockConstructor = class implements ClockInterface {
static time = 12;
currentTime = 123;
constructor(h: number, m: number) {
console.log(h, m);
}
alert(t: number) {
console.log(t);
}
};
// Generic API call
interface CountryData {
name: string;
area: number;
population: number;
}
function apiCall<T>(url: string): Promise<T> {
return fetch(url).then(resp => resp.json());
}
apiCall<CountryData>('country.json').then(data => {
console.log(data.name);
console.log(data.area);
console.log(data.population);
});
// Partial implementation
interface Person {
name: string;
age: number;
}
type PersonPartial = {
[K in keyof Person]?: Person[K];
};
// Conditional types
type User<T> = T extends { name: string }
? { name: string; age: number }
: { age: number };
type User1 = User<{ married: boolean }>;
type User2 = User<{ name: string; married: boolean }>;
// HTTP method type
type HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';
declare function fetcher<T = any>(
url: string,
method: HttpMethod,
data?: any
): Promise<T>;
declare namespace fetcher {
const get: <T = any>(url: string) => Promise<T>;
const post: <T = any>(url: string, data: any) => Promise<T>;
}
Vue 3
New Features:
Why Vue 3?
- Vue 2 limitations: Lack of abstraction for logic code
- Poor TypeScript support
Composition API:
setuprefreactive(note: loses reactivity on property access)toRefs- Lifecycle hooks
Reactive System Deep Dive:
- Stores effects for future execution
- Detects object value changes using
Proxy - Triggers stored effects when values change
Side Effects:
- Pure functions: Same input → same output, no side effects
- Side effects: Interactions with external environment
watchEffect: Automatically tracks dependencies, can be manually stoppedwatch: Precise control over effects
Custom Hooks:
- Group related features
- Highly reusible
Custom Functions Benefits:
- Clear parameter and return types
- Avoid naming conflicts
- Logic separated from components
- Generics support
- Comparison with React implementation
Additional Topics:
TeleportFragmentEmitscomponent option- Global API changes
- Syntax sugar:
<script setup>,<style vars>