一文搞定前端项目所用技术
1.vue
1.1vue2
1.2vue3
2.react
创建项目
npm create vite@latest my-app -- --template react-ts
2.1基本语法
遍历
//遍历使用map
const products = [
{ title: '卷心菜', isFruit: false, id: 1 },
{ title: '大蒜', isFruit: false, id: 2 },
{ title: '苹果', isFruit: true, id: 3 },
];
const listItems = products.map(product => {
return (
<li key={product.id} style={{color:product.isFruit? 'magenta':'darkgreen'}}>
{product.title}
</li>
)
})
return (
<>
<ul>{listItems}</ul>
</>
)
条件判断
const [state, setState] = useState(true)
return (
<>
<h1>My</h1>
<div>姓名:{name ?? "李四"}</div>
<div>年龄: {age ?? 20}</div>
<MyApp/>
<MyButton1/>
{/*条件判断*/}
{!state && <MyButton1/>}
{/*三元表达式*/}
{state ? <div>大米多多</div> : <div>大米少少</div>}
</>
组件传值props
function MyButton({count, onClick}: { count: number; onClick: () => void }) {
return (
<button onClick={onClick}>
点击{count}次
</button>
)
}
function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1)
}
return (
<div>
<h1>共同更新的计数器</h1>
<MyButton count={count} onClick={handleClick}/>
<MyButton count={count} onClick={handleClick}/>
</div>
)
}
2.2类组件(Class Component)
基本格式
//继承 React.Component 的 ES6 class
class App extends React.Component {
//固定写法,必须第一行,继承父类 React.Component 的能力,否则拿不到 this.props
constructor(props) {
super(props);
//创建 DOM / 组件引用
this.pageRef = React.createRef()
//初始化组件所有响应式数据
this.state = {
loading: false,
sendChangeId: null,
changeSettingId: null,
changeProgressId: null,
}
}
render() {
return <>
//要返回的内容
</>
}
}
export default App
修改值
this.setState({ sendChangeId: id })
render () 渲染函数(类组件必备)
生命周期
生命周期是组件从创建→渲染→更新→销毁自动执行的钩子函数:
componentDidMount:组件第一次渲染完成后执行,一般在这里发初始化接口请求加载表格数据componentDidUpdate:state/props 更新页面重渲染后执行componentWillUnmount:组件销毁前执行,清除定时器、取消请求,防止内存泄漏
2.3函数组件(Function Component)
基本格式
import React, { useState, useRef, useEffect } from 'react'
import { Button } from 'antd'
// 1. 定义函数,props接收父组件传参
const DemoPage = (props) => {
// 2. 内部写所有Hook、变量、方法(Hook必须写在最顶部,不能分支/循环里)
const [loading, setLoading] = useState(false)
const tableRef = useRef(null)
// 自定义方法
const handleClick = () => {
console.log(props.title)
}
useEffect(() => {
console.log('changeSettingId变化', changeSettingId)
}, [changeSettingId])
// 3. return 返回JSX,唯一根节点
return (
<div>
<h1>{props.title}</h1>
<Button onClick={handleClick}>点击</Button>
</div>
)
}
// 导出组件
export default DemoPage
函数组件本质就是返回 JSX 的普通 JS 函数,无class、无this、无构造函数、无生命周期。
和 Class 组件核心区别
- 没有
this,所有状态、ref、方法都是局部变量; - 靠 Hooks 管理状态、副作用、DOM 引用;
- 无生命周期函数,用
useEffect模拟挂载 / 更新 / 销毁; - 代码更简洁,无 constructor、bind 绑定 this 的麻烦;
- Hook 有严格调用规则:只能在函数组件顶层调用,不能 if/for/ 子函数内调用。
| Class 功能 | 函数组件 Hook |
|---|---|
| this.state / setState | useState |
| React.createRef | useRef |
| componentDidMount | useEffect(,[]) |
| componentDidUpdate | useEffect (,[依赖]) |
| componentWillUnmount | useEffect 返回清理函数 |
| 缓存计算值 | useMemo |
| 缓存方法 | useCallback |
| 多层传 props | useContext |
2.4路由跳转
安装
npm install react-router-dom
配置
在项目的入口文件(如
index.js或main.tsx)中,使用BrowserRouter包裹根组件App
使用(声明式,命令式)
import {useState} from 'react'
import {Routes, Route, Link, useNavigate} from 'react-router-dom';
import Home from './pages/Home';
import Detail from './pages/Detail';
import My from './pages/My';
import About from './pages/About';
import Redux from './pages/Redux';
import './App.css';
function App() {
const [count, setCount] = useState(0)
const navigate = useNavigate();
function Click() {
// 路径参数
navigate('/detail/23');
}
function ToAbout() {
//查询参数
navigate('/about?keyword=react&page=1');
}
function AddSum() {
setCount(count + 1);
}
function ToMy() {
//对象参数
navigate('my',{
state: {
name:"张三",
age:20
}
})
}
return (
<>
<div className="title">
{/*声明式跳转,适用于不带参数的直接跳转*/}
<Link to="/">首页</Link>
<Link to="/detail/23">详情</Link>
<Link to="/about">关于</Link>
<Link to="/my">我的</Link>
<Link to="/redux">状态</Link>
</div>
<div>
{/*命令式跳转 适合带参数的跳转*/}
<button onClick={Click}>点击详情</button>
<button onClick={ToAbout}>点击关于</button>
<button onClick={AddSum}>点击自增</button>
<button onClick={ToMy}>点击我的</button>
<div>{count}</div>
</div>
{/* 显示区域*/}
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/detail/:id" element={<Detail/>}/>
<Route path="/about" element={<About/>}/>
<Route path="/my" element={<My/>}/>
<Route path="/redux" element={<Redux/>}/>
</Routes>
</>
)
}
export default App
2.4.1接收参数
路径参数使用 useParams 接收
查询参数使用 使用 useSearchParams接收
对象参数使用useLocation 接收
import {useSearchParams} from 'react-router-dom'
import {Button} from "antd";
function About() {
return (
<>
<h1>About</h1>
<Search/>
<Button type="primary">Button</Button>
</>
)
}
function Search() {
//接收查询参数
const [searchParams] = useSearchParams();
const keyword = searchParams.get('keyword'); // 获取到 "react"
return <p>关键词:{keyword}</p>;
}
export default About;
import {useParams} from "react-router-dom";
function Detail() {
//接收路径参数
const { id } = useParams();
return (
<>
<h1>我是Detail页面,id是{id}</h1>
</>
)
}
export default Detail
import {useLocation } from "react-router-dom";
function My() {
//接收对象参数
const data = useLocation ()
const {name,age} = data.state?? {}
return (
<>
<h1>My</h1>
<div>姓名:{name?? "李四"}</div>
<div>年龄: {age?? 20}</div>
</>
)
}
export default My
2.4.2动态路由
2.4.3路由守卫
总结:
1. 声明式跳转:<Link> 组件
最常用在导航栏、菜单等位置,点击后直接跳转,不会刷新页面。
2. 命令式跳转:useNavigate Hook
最常用在按钮点击、表单提交成功或接口请求完成后的逻辑中。
2.5状态管理(Redux)
npm install @reduxjs/toolkit react-redux
创建状态切片
import {createSlice} from "@reduxjs/toolkit";
export const counterSlice = createSlice({
name: "counter",
initialState: {value:0,name:"存储的信息"},
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
}
}
})
//导出生成的 action creators(动作创建器)
export const {increment,decrement} = counterSlice.actions;
// 导出 reducer
export default counterSlice.reducer;
创建全局Store
import {configureStore} from "@reduxjs/toolkit";
import {counterSlice} from "./redux/counterSlice.tsx";
//使用 configureStore 创建 Store,并将上一步创建的 reducer 添加进去
export default configureStore({
reducer: {
counter: counterSlice.reducer,
}
})
在应用入口提供Store
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import {BrowserRouter} from "react-router-dom";
import {Provider} from "react-redux";
import store from "./store.tsx";
createRoot(document.getElementById('root')!).render(
// Provider 包裹 能够使用redux
<Provider store={store}>
{/* 路由包裹,能偶使用router*/}
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
)
在页面中使用:
import {useDispatch, useSelector} from "react-redux";
import {decrement, increment} from "../redux/counterSlice.tsx";
function Redux () {
// 从全局 store 中读取 count 值
const count = useSelector((state) => state.counter.value);
const name = useSelector((state) => state.counter.name);
// 获取 dispatch 函数
const dispatch = useDispatch();
return (
<div>
<h1>当前计数:{count}</h1>
<h1>姓名:{name}</h1>
<button onClick={() => dispatch(increment())}>增加</button>
<button onClick={() => dispatch(decrement())}>减少</button>
</div>
);
}
export default Redux;
核心
| 方法 / API | 所属库 | 核心作用 | 一句话总结 |
|---|---|---|---|
| createSlice | Redux Toolkit | 创建状态切片 | 自动帮你生成 action 和 reducer,你只需写更新状态的逻辑。 |
| configureStore | Redux Toolkit | 创建全局 Store | 一键创建 Store,自动配置好中间件和调试工具。 |
| Provider | React-Redux | 全局注入 Store | 在根组件包裹 <App />,让所有子组件都能访问到全局状态。 |
| useSelector | React-Redux | 读取全局状态 | 在组件里“订阅”数据,状态一变,组件自动重新渲染。 |
| useDispatch | React-Redux | 触发状态更新 | 拿到 dispatch 函数,用来派发 action,修改全局数据。 |
和localStorage对比
| 对比维度 | Redux Store | localStorage |
|---|---|---|
| 存储位置 | 浏览器的内存(RAM)中 | 浏览器的本地磁盘(ROM)中 |
| 数据持久性 | 无法持久化。页面刷新(F5)或关闭后,内存释放,数据会被重置为初始值。 | 永久持久化。除非用户手动清除或代码删除,否则关闭浏览器后数据依然存在。 |
| 响应式更新 | 具备响应式。当 Store 中的数据发生变化时,订阅了该数据的组件会自动重新渲染,视图会实时更新。 | 无响应式。数据改变后,页面视图不会自动更新。如果两个组件共用该数据,一个改了,另一个无法自动感知。 |
| 数据读取速度 | 极快(直接读取内存对象)。 | 相对较慢(需要从磁盘读取,且存取时通常需要进行 JSON 序列化和反序列化)。 |
| 核心应用场景 | 管理应用内多个组件之间需要共享且频繁变化的状态(如购物车数据、全局筛选条件)。 | 管理跨页面、跨会话的静态数据(如 Token、用户ID、菜单记忆、用户偏好设置)。 |
3.ts
4.uni-app
uni-app 是一个使用 Vue.js 语法开发前端应用的框架。它的核心优势是“一套代码,多端运行”。开发者编写一套代码后,可以将其编译发布到 iOS、Android、Web(H5)、以及各种主流小程序(微信、支付宝、抖音、百度等)和鸿蒙系统上36。它采用原生渲染,性能接近原生项目,且完全兼容 Vue2/Vue3 语法,零学习成本
4.1重要概念和语法
pages/:存放所有业务页面,页面必须在此注册路由才能访问。static/:存放图片、字体等静态资源。components/:存放全局公共组件。pages.json:重中之重,用于配置页面路由、导航栏标题、颜色以及 tabBar 等- 单位rpx
- 绑定事件用@click 或者@tap
onLoad(options):页面首次加载时触发,只执行一次。常用于初始化数据、解析 URL 参数或发起网络请求。onShow():页面每次显示时触发(包括从其他页面返回)。常用于刷新列表或更新状态。- 下拉刷新:需在
pages.json中开启enablePullDownRefresh。在页面中监听onPullDownRefresh事件,用于重置页码并重新加载第一页数据,加载完成后调用uni.stopPullDownRefresh()停止动画。 - 上拉加载更多:监听
onReachBottom事件,当页面滚动到底部时触发,用于请求下一页数据。
4.2基本语法
创建项目(vue3+ts):
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
安装依赖:
npx @dcloudio/uvm@latest
tabbar
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/cart/index",
"style": {
"navigationBarTitleText": "购物车"
}
},
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/subpage/list/list", // 页面路径是相对于 root 的,同样不写 .vue 后缀
"style": {
"navigationBarTitleText": "测试页面"
}
}
],
// "subPackages": [
// {
// "root": "subpage", // 分包的根目录
// "pages": [
// {
// "path": "list/list", // 页面路径是相对于 root 的,同样不写 .vue 后缀
// "style": {
// "navigationBarTitleText": "测试页面"
// }
// }
// ]
// }
// ],
"tabBar": {
"color": "#999999",
"selectedColor": "#007AFF",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/cart/index",
"text": "购物车"
},
{
"pagePath": "pages/my/index",
"text": "我的"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
4.3页面跳转
4.3.1保留式跳转:uni.navigateTo
-
原理:保留当前页面,将其压入页面栈(page stack),跳转到新页面。用户可以通过左滑或返回按钮回到上一页。
-
适用场景:列表页跳转到详情页、表单页跳转到确认页等所有需要支持返回的场景。
-
传值方式:直接在
url后面拼接参数(如?id=123&name=John)。 -
接收参数:在目标页面的
onLoad(options)生命周期中,通过options.id获取。
跳转代码演示:
uni.navigateTo({
url: `/pages/basedataubpage/detail/studentdetail?id=${item.id}&templateId=${templateId}&schoolId=${schoolId}`
})
接收:
onLoad(async (options: OnLoadOptions) => {
const id = options.id
const templateId = options.templateId
await getStudentInfoNameByIdInfo(id)
loadForm(id, templateId, options.schoolId)
})
4.3.2. 替换式跳转:uni.redirectTo
- 原理:关闭当前页面,再打开新页面。当前页面会被销毁,用户在新页面无法通过返回键回到被关闭的那一页。
- 适用场景:登录成功后跳转首页、支付成功页、注册完成页等“一次性”操作完成后的跳转。
4.3.3. 全清重置跳转:uni.reLaunch
- 原理:关闭应用内所有页面后,打开指定页面。页面栈被完全清空,是最彻底的跳转方式。
- 适用场景:退出登录、切换账号、应用重置。
4.3.4 Tab 页跳转:uni.switchTab
- 原理:专门用于跳转到
pages.json中配置了tabBar的页面。跳转时会关闭所有非 tabBar 页面。 - 适用场景:底部 Tab 切换、业务流程结束回到主页。
- 注意:
switchTab的url不能带参数。如果需要向 Tab 页传递数据,可以通过全局变量(globalData)、Pinia/Vuex 或 Storage 中转。
4.3.5. 返回上级:uni.navigateBack
- 原理:关闭当前页面,返回上一页或多级页面。
- 适用场景:表单取消、弹窗关闭、跨级返回。
4.4组件传值
4.4.1父传子(Props)
子组件:
<template>
<view>{{title}}</view>
<view>{{count}}</view>
</template>
<script lang="ts" setup>
const props = defineProps({
title: {
type: String,
default: '默认标题'
},
count: {
type: Number,
default: 0
}
})
</script>
<style scoped>
</style>
父组件:
<template>
<view>
<button @tap ="gotoPage">点击跳转到列表页</button>
</view>
<view>
<ChildComponent :title="title" :count="count"/>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const title = ref('Hello')
const count = ref(1222)
const gotoPage = () => {
uni.navigateTo({
url: '/pages/subpage/list/list'
})
}
</script>
<style>
</style>
4.4.2子传父(emits)
子组件:
<template>
<button @click="sendData">点击向父组件发送数据</button>
</template>
<script lang="ts" setup>
//定义要触发的事件名称
const emits = defineEmits(['sendData'])
//触发事件,并传递参数
const sendData = () => {
emits(`sendData`,{name:'Hello',age:25})
}
</script>
<style scoped>
</style>
父组件:
<template>
<view>
<button @tap ="gotoPage">点击跳转到列表页</button>
</view>
<view>
<!-- 父传子-->
<ChildComponent :title="title" :count="count"/>
<!-- 子传父-->
<child-component1 @sendData="handleChildData"/>
<!-- 兄弟相传/跨页面-->
<ChildComponent2/>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
import ChildComponent1 from './ChildComponent1.vue'
import ChildComponent2 from '/src/pages/cart/Component2.vue'
const title = ref('Hello')
const count = ref(1222)
const handleChildData = (data:any) => {
console.log("子组件传过来的值",data)
}
const gotoPage = () => {
uni.navigateTo({
url: '/pages/subpage/list/list'
})
}
</script>
<style>
</style>
4.4.3兄弟/不同页面(uni.$emit,uni.$on,uni.$off)
A组件:
<template>
<button @click="notifyBrother">跨页面/兄弟组件传值</button>
</template>
<script lang="ts" setup>
const notifyBrother = () => {
//全局触发事件
uni.$emit('sync-user-info',{userId:1,userName:"张三"})
}
</script>
<style scoped>
</style>
B组件:
<template>
<view>我的购物车页面</view>
<text>接受的用户数据是:{{userInfo}}</text>
</template>
<script lang="ts" setup>
import {ref,onMounted,onUnmounted} from 'vue'
const userInfo = ref(null);
//接收到的数据
const handleSync = (data:any) => {
userInfo.value = data;
console.log("接收到的数据",data);
}
//在组件挂载的时候监听
onMounted(() => {
uni.$on('sync-user-info', handleSync);
})
//卸载前移除监听
onUnmounted(() => {
uni.$off('sync-user-info', handleSync);
})
</script>
<style scoped>
</style>
4.4.4 v-model
子组件:
<template>
<view>
<input :value="modelValue" @input="handleInput" placeholder="请输入内容">
</view>
</template>
<script lang="ts" setup>
const props = defineProps({
modelValue: { //必须叫modelValue
type: String,
default: '',
}
})
//触发事件
const emit = defineEmits(['update:modelValue'])
const handleInput = (e:any) => {
//将新值通过事件回传给父组件
emit('update:modelValue', e.detail.value)
}
</script>
<style scoped>
</style>
父组件:
<template>
<view>我的页面</view>
<!-- 自定义组件支持 v-model-->
<ChildComponent3 v-model="inputValue"/>
<text>输入的值是:{{inputValue}}</text>
<!-- 父类直接操作子类的方法-->
<Component4 ref="childRef"/>
<button @click="callChild"> 调用子组件的方法</button>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import ChildComponent3 from './Component3.vue'
const inputValue = ref("")
import Component4 from './Component4.vue'
//创建一个ref来接收子组件的实例
const childRef = ref(null)
const callChild = () =>{
childRef.value.childMethod("我是父组件传来的参数")
}
</script>
<style scoped>
</style>
4.4.5 ref
<template>
<view>
<text>子组件内容</text>
</view>
</template>
<script lang="ts" setup>
const childMethod = (msg: string) => {
console.log("父组件调用我的方法",msg)
}
//将方法暴漏出去
defineExpose(
{
childMethod
}
)
</script>
<style scoped>
</style>
4.5常用方法
下拉刷新/上拉加载更多
在 pages.json 中开启配置
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true, // 开启下拉刷新
"onReachBottomDistance": 50 // 距离底部 50px 时触发上拉加载
}
},
{
"path": "pages/cart/index",
"style": {
"navigationBarTitleText": "购物车"
}
},
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/subpage/list/list", // 页面路径是相对于 root 的,同样不写 .vue 后缀
"style": {
"navigationBarTitleText": "测试页面"
}
}
],
在代码处导入方法
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
//下拉刷新事件
onPullDownRefresh(() => {
})
//上拉触底事件
onReachBottom(() => {
})
4.6uni-app x
5.组件库
5.1.移动端
安装
pnpm add @wot-ui/ui
pnpm add sass@^1.98.0 -D
配置
//pages.json
"easycom": {
"autoscan": true,
"custom": {
"^wd-(.*)": "@wot-ui/ui/components/wd-$1/wd-$1.vue",
}
},
使用
<wd-button>主要按钮</wd-button>
<wd-button type="success">成功按钮</wd-button>
<wd-button type="info">信息按钮</wd-button>
<wd-button type="warning">警告按钮</wd-button>
<wd-button type="danger">危险按钮</wd-button>

安装
pnpm add @dcloudio/uni-ui
配置
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
使用
<uni-card>
<text>这是一个基础卡片示例,内容较少,此示例展示了一个没有任何属性不带阴影的卡片。</text>
</uni-card>

5.2.Web端
Ant Design - 一套企业级 UI 设计语言和 React 组件库
pnpm install antd --save
pnpm install element-plus
6.前端全栈
6.1next.js
6.2nuxt3
6.3express(node的后端框架)
更多推荐




所有评论(0)