[Medium] 📄 Vue 基础与 API
1. Can you describe the core principles and advantages of the framework Vue?
请描述 Vue 框架的核心原理和优势
核心原理
Vue 是一个渐进式的 JavaScript 框架,其核心原理包含以下几个重要概念:
1. 虚拟 DOM(Virtual DOM)
使用虚拟 DOM 来提升性能。它只会更新有变化的 DOM 节点,而不是重新渲染整个 DOM Tree。通过 diff 算法比较新旧虚拟 DOM 的差异,只针对差异部分进行实际 DOM 操作。
// 虚拟 DOM 概念示意
const vnode = {
tag: 'div',
props: { class: 'container' },
children: [
{ tag: 'h1', children: 'Hello' },
{ tag: 'p', children: 'World' },
],
};
2. 数据双向绑定(Two-way Data Binding)
使用双向数据绑定,当模型(Model)更改时,视图(View)会自动更新,反之亦然。这让开发者不需要手动操作 DOM,只需关注数据的变化。
<!-- Vue 3 推荐写法:<script setup> -->
<template>
<input v-model="message" />
<p>{{ message }}</p>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('Hello Vue');
</script>
Options API 写法
<template>
<input v-model="message" />
<p>{{ message }}</p>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue',
};
},
};
</script>
3. 组件化(Component-based)
将整个应用拆分成一个个组件,意味着复用性提升,这对维护开发会更加省工。每个组件都有自己的状态、样式和逻辑,可以独立开发和测试。
<!-- Button.vue - Vue 3 <script setup> -->
<template>
<button @click="handleClick">
<slot></slot>
</button>
</template>
<script setup>
const emit = defineEmits(['click']);
const handleClick = () => {
emit('click');
};
</script>
4. 生命周期(Lifecycle Hooks)
有自己的生命周期,当数据发生变化时,会触发相应的生命周期钩子,这样就可以在特定的生命周期中,做出相应的操作。
<!-- Vue 3 <script setup> 写法 -->
<script setup>
import { onMounted, onUpdated, onUnmounted } from 'vue';
onMounted(() => {
// 组件挂载后执行
console.log('Component mounted!');
});
onUpdated(() => {
// 数据更新后执行
console.log('Component updated!');
});
onUnmounted(() => {
// 组件卸载后执行
console.log('Component unmounted!');
});
</script>
5. 指令系统(Directives)
提供了一些常用的指令,例如 v-if、v-for、v-bind、v-model 等,可以让开发者更快速地开发。
<template>
<!-- 条件渲染 -->
<div v-if="isVisible">显示内容</div>
<!-- 列表渲染 -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
<!-- 属性绑定 -->
<img :src="imageUrl" :alt="imageAlt" />
<!-- 双向绑定 -->
<input v-model="username" />
</template>
6. 模板语法(Template Syntax)
使用 template 来编写 HTML,允许将数据通过插值的方式,直接渲染到 template 中。
<template>
<div>
<!-- 文字插值 -->
<p>{{ message }}</p>
<!-- 表达式 -->
<p>{{ count + 1 }}</p>
<!-- 方法调用 -->
<p>{{ formatDate(date) }}</p>
</div>
</template>
Vue 的独有优势(和 React 相比)
1. 学习曲线较低
对团队成员彼此程度的掌控落差不会太大,同时在书写风格上,由官方统一规定,避免过于自由奔放,同时对不同项目的维护也能更快上手。
<!-- Vue 的单文件组件结构清晰 -->
<template>
<!-- HTML 模板 -->
</template>
<script>
// JavaScript 逻辑
</script>
<style>
/* CSS 样式 */
</style>
2. 拥有自己的独特指令语法
虽然这点可能见仁见智,但 Vue 的指令系统提供 了更直观的方式来处理常见的 UI 逻辑:
<!-- Vue 指令 -->
<div v-if="isLoggedIn">欢迎回来</div>
<button @click="handleClick">点击</button>
<!-- React JSX -->
<div>{isLoggedIn && '欢迎回来'}</div>
<button onClick="{handleClick}">点击</button>
3. 数据双向绑定更容易实现
因为有自己的指令,所以开发者实现数据双向绑定可以非常容易(v-model),而 React 虽然也能实现类似的功能,但没有 Vue 来得直觉。
<!-- Vue 双向绑定 -->
<input v-model="username" />
<!-- React 需要手动处理 -->
<input value={username} onChange={(e) => setUsername(e.target.value)} />
4. 模板和逻辑分离
React 的 JSX 仍为部分开发者所诟病,在部分开发场景下,将逻辑和 UI 进行分离会显得更易阅读与维护。
<!-- Vue:结构清晰 -->
<template>
<div class="user-card">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: 'John',
email: 'john@example.com',
},
};
},
};
</script>
5. 官方生态系统完整
Vue 官方提供了完整的解决方案(Vue Router、Vuex/Pinia、Vue CLI),不需要在众多第三方包中选择。
2. Please explain the usage of v-model, v-bind and v-html
请解释
v-model、v-bind和v-html的使用方式
v-model:数据双向绑定
当改变数据的同时,随即驱动改变 template 上渲染的内容,反之改变 template 的内容,也会更新数据。
<template>
<div>
<!-- 文字输入框 -->
<input v-model="message" />
<p>输入的内容:{{ message }}</p>
<!-- 复选框 -->
<input type="checkbox" v-model="checked" />
<p>是否勾选:{{ checked }}</p>
<!-- 选项列表 -->
<select v-model="selected">
<option value="A">选项 A</option>
<option value="B">选项 B</option>
</select>
<p>选择的选项:{{ selected }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: '',
checked: false,
selected: 'A',
};
},
};
</script>
v-model 的修饰符
<!-- .lazy:改为在 change 事件后更新 -->
<input v-model.lazy="msg" />
<!-- .number:自动转为数字 -->
<input v-model.number="age" type="number" />
<!-- .trim:自动过滤首尾空白字符 -->
<input v-model.trim="msg" />
v-bind:动态绑定属性
常见于绑定 class 或链接、图片等。当通过 v-bind 绑定 class 后,可以通过数据变动,来决定该 class 样式是否被绑定,同理 API 返回的图片路径、链接网址,也能通过绑定的形式来维持动态更新。
<template>
<div>
<!-- 绑定 class(可以简写为 :class) -->
<div :class="{ active: isActive, 'text-danger': hasError }">动态 class</div>
<!-- 绑定 style -->
<div :style="{ color: textColor, fontSize: fontSize + 'px' }">动态样式</div>
<!-- 绑定图片路径 -->
<img :src="imageUrl" :alt="imageAlt" />
<!-- 绑定链接 -->
<a :href="linkUrl">前往链接</a>
<!-- 绑定自定义属性 -->
<div :data-id="userId" :data-name="userName"></div>
</div>
</template>
<script>
export default {
data() {
return {
isActive: true,
hasError: false,
textColor: 'red',
fontSize: 16,
imageUrl: 'https://example.com/image.jpg',
imageAlt: '图片描述',
linkUrl: 'https://example.com',
userId: 123,
userName: 'John',
};
},
};
</script>
v-bind 的简写
<!-- 完整写法 -->
<img v-bind:src="imageUrl" />
<!-- 简写 -->
<img :src="imageUrl" />
<!-- 绑定多个属性 -->
<div v-bind="objectOfAttrs"></div>
v-html:渲染 HTML 字符串
如果数据返回的内容中带有 HTML 的标签时,可以通过这个指令来渲染,例如显示 Markdown 语法又或是对方直接返回含有 <img> 标签的图片路径。
<template>
<div>
<!-- 普通插值:会显示 HTML 标签 -->
<p>{{ rawHtml }}</p>
<!-- 输出:<span style="color: red">红色文字</span> -->
<!-- v-html:会渲染 HTML -->
<p v-html="rawHtml"></p>
<!-- 输出:红色文字(实际渲染为红色) -->
</div>
</template>
<script>
export default {
data() {
return {
rawHtml: '<span style="color: red">红色文字</span>',
};
},
};
</script>
⚠️ 安全性警告
千万不要对用户提供的内容使用 v-html,这会导致 XSS(跨站脚本攻击)漏洞!
<!-- ❌ 危险:用户可 以注入恶意脚本 -->
<div v-html="userProvidedContent"></div>
<!-- ✅ 安全:只用于可信任的内容 -->
<div v-html="markdownRenderedContent"></div>
安全的替代方案
<template>
<div>
<!-- 使用库进行 HTML 净化 -->
<div v-html="sanitizedHtml"></div>
</div>
</template>
<script>
import DOMPurify from 'dompurify';
export default {
data() {
return {
userInput: '<img src=x onerror=alert("XSS")>',
};
},
computed: {
sanitizedHtml() {
// 使用 DOMPurify 清理 HTML
return DOMPurify.sanitize(this.userInput);
},
},
};
</script>