Vue和React作为两大主流框架,其核心概念与工具的理解和运用对开发者至关重要。
本文将深入探讨
- Vue和React的组件化思想
- CLI命令行工具以及相关项目结构与关键文件,
入门React 和 Vue 两个框架
一、组件与模块
模块
它是按照特定功能将JavaScript代码划分成的独立单元,其核心目标在于提升代码的可维护性与复用性,有效避免代码的混乱与冲突。
就好比在一个大型建筑项目中,将水电系统、通风系统等分别封装成独立模块,便于管理与维护。
在JavaScript生态中,常见的模块规范有CommonJS和ES Modules。以ES Modules为例,假设我们有一个math.js文件作为模块,用于封装数学运算相关的函数:
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
在其他文件中,我们可以轻松导入并使用这些函数,如下所示:
// app.js
import { add, subtract } from './math.js';
console.log(add(5, 3));
console.log(subtract(5, 3));
组件
组件则是前端开发的核心构建模块,它将JavaScript、HTML、CSS以及可能涉及的资源(如图标、字体等)整合在一起,形成一个独立且功能完整的单元。
组件是现代前端开发的新范式,其公式“组件 = JavaScript + CSS + 有一个逻辑单元的HTML + 资源”精准概括了其构成要素。
以一个常见的登录组件为例,HTML部分负责构建登录表单的结构:
<form>
<label for="username">Username:</label>
<input type="text" id="username" />
<label for="password">Password:</label>
<input type="password" id="password" />
<button type="submit">Login</button>
</form>
CSS部分用于美化登录表单的样式,使其在视觉上更吸引人:
form {
width: 300px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
}
JavaScript部分则处理登录逻辑,如表单验证、提交数据等:
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
// 这里可以添加登录验证逻辑,如发送请求到服务器验证用户信息
console.log(`Logging in with username: ${username} and password: ${password}`);
});
将上述代码封装成一个组件后,就可以在多个页面中复用,极大提高了开发效率。
也就是说:
- 模块是按照功能将 js 分开组成模块
- 组件是按照功能将 js + html +css + 资源(比如: 图片、音频)封装成一个组件
Vue组件化思想与实践
Vue组件的构成与创建
Vue组件通过独特的.vue文件格式进行定义,这种方式将模板(HTML)、脚本(JavaScript)和样式(CSS)清晰地分离,同时又紧密结合在一起。一个典型的.vue文件包含三个部分:
<template>
<!-- 这里放置HTML模板 -->
</template>
<script>
export default {
// 这里编写JavaScript逻辑,包括数据定义、方法等
};
</script>
<style>
/* 这里定义CSS样式 */
</style>
例如,创建一个简单的计数器组件:
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
}
}
};
</script>
<style>
p {
font-size: 18px;
}
button {
background-color: #4CAF50;
color: white;
padding: 5px 10px;
border: none;
border-radius: 3px;
cursor: pointer;
}
</style>
Vue组件的功能划分与复用
Vue组件严格按照功能进行划分,这使得项目结构清晰明了,无论是开发过程还是后续维护都变得更加轻松。
就像构建一座房子,由卧室、厕所、大厅等功能明确的区域组成一样,一个Web应用也由众多功能专一的组件构建而成。
以一个电商应用为例,商品列表组件、商品详情组件、购物车组件等各司其职。这些组件可以在不同的页面或场景中灵活复用,极大提高了代码的复用性。
例如,商品列表组件可以在首页、分类页面等多个地方展示,只需根据不同页面的需求传递相应的数据即可。
Vue组件在工程中的优势
从工程化的角度来看,Vue组件具有诸多显著优势。按功能划分组件使得项目结构清晰有序,开发者能够轻松安排开发任务,团队成员之间也可以更高效地分工协作。
不同开发者专注于不同组件的开发,有效提高了开发效率。同时,组件的复用性减少了重复开发的工作量,降低了维护成本。当需要修改某个功能时,只需在对应的组件中进行调整,不会影响整个应用程序。
而且,Vue组件以标签的方式嵌入页面,如<Counter/>,这种方式使页面结构一目了然,可读性极佳,方便其他开发者理解和维护代码。
React组件化思想与实践
React组件的定义与类型
React组件主要有函数组件和类组件两种形式。函数组件简洁高效,专注于UI展示,在React 16.8引入Hooks后,其功能得到了极大的拓展,能够处理状态管理和副作用等操作。例如,以下是一个简单的函数组件用于显示欢迎消息:
function WelcomeMessage(props) {
return <h1>Welcome, {props.name}!</h1>;
}
类组件则提供了更丰富的生命周期方法和状态管理能力,适用于复杂的业务逻辑场景。例如,一个类组件用于管理计数器状态:
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment() {
this.setState((prevState) => ({
count: prevState.count + 1
}));
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={() => this.increment()}>Increment</button>
</div>
);
}
}
React组件的通信机制
React组件间通信方式多样且灵活。在父子组件通信中,父组件向子组件传递数据通过props实现,子组件接收props并渲染相应内容。例如:
// 父组件
class ParentComponent extends Component {
constructor() {
super();
this.state = {
message: 'Hello from parent!'
};
}
render() {
return (
<div>
<ChildComponent message={this.state.message} />
</div>
);
}
}
// 子组件
function ChildComponent(props) {
return <div>{props.message}</div>;
}
子组件向父组件传递数据则通过父组件传递回调函数,子组件在适当的时候调用该回调函数。例如:
// 父组件
class ParentComponent extends Component {
constructor() {
super();
this.state = {
childMessage: ''
};
}
handleChildMessage(message) {
this.setState({
childMessage: message
});
}
render() {
return (
<div>
<ChildComponent onMessage={this.handleChildMessage} />
<p>Received from child: {this.state.childMessage}</p>
</div>
);
}
}
// 子组件
function ChildComponent(props) {
const handleClick = () => {
props.onMessage('Hello from child!');
};
return <button onClick={handleClick}>Send Message to Parent</button>;
}
对于非父子组件通信,React提供了Context API,用于跨组件层级共享状态,适用于全局状态管理场景,如主题切换、用户认证信息等。例如:
// 创建Context
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = React.useContext(ThemeContext);
return <button style={{ backgroundColor: theme === 'dark'? 'black' : 'white' }}>Click me</button>;
}
React组件的生命周期(类组件)
React类组件具有完整的生命周期方法,涵盖挂载(Mount)、更新(Update)和卸载(Unmount)阶段。
在挂载阶段,constructor用于初始化组件状态和绑定事件处理函数(可选),render方法返回要渲染的React元素,componentDidMount在组件挂载后立即调用,适合进行依赖DOM的操作、发送网络请求或添加订阅。例如:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
componentDidMount() {
// 发送网络请求获取数据
fetch('https://example.com/api/data')
.then((response) => response.json())
.then((data) => this.setState({ data }));
}
render() {
return <div>{this.state.data? this.state.data : 'Loading...'}</div>;
}
}
在更新阶段,componentDidUpdate在组件更新后调用,可在此对DOM进行操作或根据props变化决定是否发送网络请求。例如:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidUpdate(prevProps) {
if (prevProps.count!== this.props.count) {
// 根据props变化进行操作
console.log('Count has changed');
}
}
handleClick() {
this.props.onIncrement();
}
render() {
return (
<div>
<p>Count: {this.props.count}</p>
<button onClick={() => this.handleClick()}>Increment</button>
</div>
);
}
}
在卸载阶段,componentWillUnmount在组件卸载及销毁之前直接调用,用于执行清理操作,如清除定时器、取消网络请求或订阅等。例如:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
timer: null
};
}
componentDidMount() {
const timer = setInterval(() => {
console.log('Timer ticking');
}, 1000);
this.setState({ timer });
}
componentWillUnmount() {
clearInterval(this.state.timer);
}
render() {
return <div>My Component</div>;
}
}
Vue与React CLI命令行工具
Vue CLI命令行
Vue CLI是Vue.js官方提供的强大脚手架工具,主要用于快速搭建Vue项目,尤其适用于复杂大型项目的开发。它提供了丰富的预设配置和插件,方便开发者根据项目需求进行定制化开发。
使用vue create命令可以创建一个新的Vue项目,例如vue create my-vue-app。在创建过程中,开发者可以选择不同的预设配置,如默认配置、手动选择特性(包括是否使用TypeScript、Router、Vuex、CSS预处理器等)、使用历史模式的Router等。
React CLI命令行
React提供了create-react-app命令行工具,用于快速创建React项目。这个工具简化了项目的初始搭建过程,为开发者生成一个包含基本配置和文件结构的React应用模板。
例如,在命令行中执行npx create-react-app my-react-app,将会创建一个名为my-react-app的新React项目。
项目结构与关键文件对比
1. package.json
在Vue和React项目中,package.json都是核心配置文件。在Vue项目中,它记录项目信息、管理依赖关系等。开发依赖可能包括@vue/cli-service等用于构建和开发的工具,生产依赖则包含Vue核心库以及项目中实际使用的其他库。例如:
{
"name": "my-vue-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"vue": "^3.3.4"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-plugin-eslint": "^5.0.8",
"@vue/cli-service": "^5.0.8",
"eslint": "^8.45.0",
"eslint-plugin-vue": "^9.17.0"
}
}
在React项目中,package.json同样记录项目基本信息、依赖关系等。开发依赖如react-scripts用于启动开发服务器、构建项目等,生产依赖主要是React相关的核心库,如react和react-dom。例如:
{
"name": "my-react-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"react-scripts": "^5.0.1"
}
}
2. 入口文件
Vue项目的入口文件通常是src/main.js,主要负责创建Vue应用实例并挂载到DOM上。例如:
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.mount('#app');
React项目的入口文件一般是src/index.js,在这个文件中,主要进行React应用的渲染操作,通过ReactDOM.render方法将React组件挂载到index.html中的根DOM节点上。例如:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
3. 根组件
Vue的根组件是src/App.vue,采用三段式结构,分别负责模板、逻辑和样式。例如:
<template>
<div>
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!'
};
}
};
</script>
<style>
h1 {
color: blue;
}
</style>
React的根组件可以是src/App.js,可以是函数组件或类组件形式。函数组件示例:
function App() {
return (
<div>
<h1>Hello, React!</h1>
</div>
);
}
export default App;
类组件示例:
// 1.创建类组件
class MyCompont extends React.Component{
render() {
// render是放在哪里的 ? - 类MyCompont的原型对象上 , 供实例调用 , 那实例在哪里 ?没有 new MyComponent() ?
// render中的this 是谁 ? - MyComponent 的实例对象 。
console.log(this)
return <h2>hello , 我是用函数定义的组件</h2>
}
}
// 2. 渲染组件到页面
ReactDOM.render(<MyCompont/>,document.getElementById('test'))
/*
执行了ReactDoM.render(<MyComponent>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出该类的实例出来,并通过该实例调用原型上的render 方法
3.将返回的虚拟DOM转为真实DOM,随后呈现在页面中
*/
组件化在Vue与React中的共性与差异
1. 共性
- 提升开发效率:Vue和React的组件化均能有效提升开发速度。以构建一个大型电商应用为例,无论是Vue还是React,都可将商品列表、购物车、用户信息等模块拆分成独立组件。开发者能并行开发这些组件,避免重复劳动,显著缩短项目周期。比如,在多个页面均需使用的商品搜索组件,只需开发一次,即可在不同页面复用,大大提高了开发效率。
- 增强代码维护性:两种框架的组件化都使项目结构清晰,职责分明。当应用规模增大时,如一个复杂的企业级应用,无论是基于Vue还是React开发,若某个组件出现问题,都能迅速定位到对应的组件代码进行修复,而不会牵一发而动全身。例如,若订单处理组件发生故障,只需关注该组件的逻辑,不会影响其他无关功能。
- 优化代码复用性:在Vue和React中,组件的复用性都很强。无论是小型项目中的通用按钮组件,还是大型项目中的复杂布局组件,都可在不同场景下重复使用。只需根据具体需求传递不同参数,就能适应各种变化。例如,一个弹窗组件,可在不同页面用于提示信息、确认操作等,只需调整显示内容和样式参数。
2. 差异
- 组件定义形式:Vue采用独特的
.vue文件格式定义组件,将模板、逻辑和样式整合在一个文件中,结构清晰明了。例如:
<template>
<div>
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
title: 'Vue Component'
};
}
};
</script>
<style>
h1 {
color: blue;
}
</style>
React则有函数组件和类组件两种形式。函数组件简洁灵活,适用于简单UI展示,如:
function MyComponent(props) {
return <h1>{props.title}</h1>;
}
类组件提供更多生命周期方法和状态管理功能,适合复杂逻辑处理,如:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
title: 'React Component'
};
}
render() {
return <h1>{this.state.title}</h1>;
}
}
- 模板语法特点:Vue使用基于HTML的模板语法,通过指令实现数据绑定和事件处理,符合传统前端开发者习惯。例如,使用
v-bind绑定属性,v-on绑定事件:
<template>
<div>
<input v-model="message" />
<button v-on:click="sendMessage">Send</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!'
};
},
methods: {
sendMessage() {
console.log(this.message);
}
}
};
</script>
React使用JSX语法,将HTML与JavaScript融合,更具灵活性。例如:
function InputWithButton(props) {
const handleClick = () => {
console.log(props.value);
};
return (
<div>
<input value={props.value} onChange={props.onChange} />
<button onClick={handleClick}>Send</button>
</div>
);
}
- 状态管理模式:Vue除了组件内部
data选项管理状态外,还提供Vuex用于全局状态管理,其遵循严格的状态变更流程,包含状态、突变、动作和获取器等概念。例如:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
}
});
// 在组件中使用
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex';
export default {
computed: {
...mapState(['count']),
...mapGetters(['doubleCount'])
},
methods: {
...mapMutations(['increment']),
...mapActions(['incrementAsync'])
}
};
React主要通过setState(类组件)和useState、useReducer(函数组件)管理状态,对于跨组件状态共享,使用Context API。例如:
// 使用useState
import React, { useState } from'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
// 使用Context API
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = React.useContext(ThemeContext);
return <button style={{ backgroundColor: theme === 'dark'? 'black' : 'white' }}>Click me</button>;
}
- 组件通信方式:在父子组件通信上,Vue和React类似,父组件通过属性传递数据给子组件,子组件通过事件触发父组件函数来传递消息。但在非父子组件通信方面,Vue提供
EventBus(简单事件总线,适用于小型项目)和Vuex(用于中大型项目全局状态管理),而React主要依赖ContextAPI,同时也可通过状态提升等方式实现有限的非父子组件通信。例如,在Vue中使用EventBus:
// EventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
// 发送事件
EventBus.$emit('message', 'Hello from EventBus');
// 接收事件
EventBus.$on('message', (data) => {
console.log(data);
});
组件化思想
组件化在前端开发中十分重要 。
在开发效率提升上,组件化允许开发者将复杂页面拆分成多个小而独立的组件,并行开发。
例如,在开发一个大型新闻资讯平台时,新闻列表、新闻详情、侧边栏等组件可由不同开发者同时开发,大大加快项目进度。
同时,复用已有的组件减少了重复编写代码的工作量,如通用的分页组件可在多个列表页面使用。
对于代码维护而言,清晰的组件结构使代码易于理解和修改。当需求变更时,只需关注相关组件。
例如,若新闻平台的页面布局调整,只需修改布局相关组件,不会影响其他功能组件。
在团队协作方面,组件化明确了各成员的职责范围。不同成员专注于特定组件开发,降低了代码冲突风险,提高了协作效率。
比如,前端团队中,有的成员负责用户界面组件,有的负责数据交互组件。
从代码复用角度看,组件可在不同项目或场景中重复使用,提高了代码的利用率和一致性。
例如,一个通用的模态框组件可应用于不同类型的项目中,只需根据项目风格调整样式。