Vue与React基础开发知识点总结,如组件编写方法、权限校验方式、监听变量改变等,极其适用于后端工程师。文章结尾有极度提高开发效率的彩蛋(二次封装基于antd的前端初始化模板)。

YxinMiracle 2024-07-23 12:03:01 阅读 82

文章目录

Vue相关内容总结最佳实践Vue相关开发语法组件的编写权限校验监听页面中的某个变量是否有改变监听好几个变量是否有改变所做的操作需要Dom元素加载完后才执行的逻辑

React相关内容总结最佳实践React相关开发语法定义页面变量定义组件监听变量是否有改变所做的操作

✈ 版本要求 & 特性📦 安装教程🛰 相关特性1. 权限控制2. 权限赋予3. OpenAPI配置4. 动态主题变化5. 全局配置悬浮

🚄 更改项目名字🛣️ 相关文档

Vue相关内容总结

1.Element Ui Plus - 基于Vue3的前端主键

2.AcroDesign - 有Vue和React版本,字节研发

最佳实践

1.AcroDesign最佳实践

image-20240716163735718

Vue相关开发语法

组件的编写

**场景:**有一个Markdown编辑器在项目中需要使用到,没有必要在每个页面都使用相同的代码,可以把这个Markdown编辑器抽离出来作为单独的组件。

**前提:**我们称调用这个组件的界面为父页面,这个Markdown组件为子页面。

**需要考虑到的问题:**父页面出于业务的需要,我们是需要实时的获取子页面Markdown组件用户所编写的内容去做对应的逻辑处理。

这里所使用到的MarkDown编辑器为**BtyeMD** - 一款字节出品的MarkDown编辑器。

对于子组件来说:

<code><template>

<Editor

:value="value"code>

:mode="mode"code>

style="height: 100%"code>

:plugins="plugins"code>

@change="handleChange"code>

/>

</template>

这里我们需要考虑到三个问题来提高组件的通用性:

这个组件所展示的内容,是需要父组件传递过来的,也就是value这个组件的模式,比如说:可编辑只可观看这样的模式,也是需要父组件进行传递过来的,也就是上面代码中的mode。最重要的一点,我们如何获取到用户输入到的内容,这个组件提供了一个@change属性来监听用户所输入到的内容,这个属性是一个函数,所以我们需要父组件传递过来一个函数,我们再通过子组件传递回去,也就是上诉代码中的mode

知道了需求过后,我们需要编写属于这个组件的属性:

/**

* 定义组件属性的类型

*/

interface Props {

value: string;

mode?: string;

handleChange: (value: string) => void;

}

并且,需要给这些类型进行初始化,方式父组件没有传递导致错误:

import { defineProps, withDefaults } from "vue";

// 设定默认值

const props = withDefaults(defineProps<Props>(), {

value: () => "",

mode: "split",

handleChange: (value: string) => {

console.log(value);

},

});

定义好这些内容之后,可以让父组件进行调用相关的内容,

<MdEditor

:value="form.answer"code>

:handleChange="onAnswerChange"code>

></MdEditor>

这里我们看见并没有传递mode属性,原因是子组件默认了modelsplit,所以我们可以选择不传递这个属性值。

在这个场景中,我们在form表达中使用了这个主键,这个form表单的属性包括:

const form = ref<QuestionAddRequest>({

answer: "",

....

});

onAnswerChange函数为实时改变form中的answer值:

const onAnswerChange = (v: string) => {

form.value.answer = v;

};

那么通过这样的方式我们就完成了一个Vue组件的编写。

权限校验

在前端开发中除了页面的编写还有如何使用用户的状态,这里的状态分为登录权限

在vue中我使用Vuex来保存用户的状态

export default {

namespaced: true,

state: () => ({

loginUser: { // 初始化状态

userName: "未登录",

},

}),

actions: {

async getLoginUser({ commit, state }, payload) {

// const products = await

const res = await UserControllerService.getLoginUserUsingGet();

if (res.code === 0) {

commit("updateUser", res.data); // 如果登录成功,那么保存后端传递过来的值

} else {

commit("updateUser", { // 如果登录失败,把用户的权限状态设定为未登录

...state.loginUser,

userRole: ACCESS_ENUM.NOT_LOGIN,

});

}

},

},

mutations: {

// 修改状态变量的

updateUser(state, payload) {

state.loginUser = payload;

},

},

} as StoreOptions<any>;

那么,在用户登录的时候就可以调用Vuex来存储对应的用户变量:

// 处理登录请求

const handleSubmit = async () => {

const res = await UserControllerService.userLoginUsingPost(loginForm);

if (res.code === 0) {

await store.dispatch("user/getLoginUser"); // 这一步,存储用户的登录状态

Message.success("登录成功");

} else {

Message.error("登录失败" + res.message);

}

};

那么,我们可以在main.ts中来引入自定义编写的权限校验文件,在路由中,我们可以定义每个页面可以允许哪些权限的人进行访问,路由的代码定义如下,我们在meta属性中行定义了一个access属性,我们后续会使用这个属性来进行权限的控制。

// 定义路由信息

export const routes: Array<RouteRecordRaw> = [

{

path: "/view/question/:id",

name: "在线作题",

component: ViewQuestionsView,

props: true,

meta: {

hideInMenu: true,

access: AccessEnum.USER,

},

},

];

首先我们可以在我们的项目的src目录下创建一个access文件夹,我们创建三个文件

image-20240716173203976

第一个文件<code>accessEnum.ts,用来定义系统中存在哪些权限的用户,以本系统举例:

const ACCESS_ENUM = {

NOT_LOGIN: "notLogin",

USER: "user",

ADMIN: "admin",

};

export default ACCESS_ENUM;

第二个文件checkAccess.ts,定义了这个用户是否符合权限

import ACCESS_ENUM from "@/access/accessEnum";

const checkAccess = (loginUser: any, needAccess = ACCESS_ENUM.NOT_LOGIN) => {

const loginUserAccess = loginUser?.userRole ?? ACCESS_ENUM.NOT_LOGIN;

// 获取当前用户具有的权限,没有loginUser表示未登录

if (needAccess === ACCESS_ENUM.NOT_LOGIN) {

return true;

}

if (needAccess === ACCESS_ENUM.USER) {

// todo 只需要判断登录就可以

console.log(">>>>", loginUserAccess);

if (loginUserAccess === ACCESS_ENUM.NOT_LOGIN) {

return false;

}

}

if (needAccess === ACCESS_ENUM.ADMIN) {

// 需要管理员权限但是不为管理员

if (loginUserAccess !== ACCESS_ENUM.ADMIN) {

return false;

}

}

return true;

};

export default checkAccess;

第三个文件index.ts,定义了校验逻辑,当路由需要开始进行转变的时候:

import router from "@/router";

import store from "@/store";

import ACCESS_ENUM from "@/access/accessEnum";

import { routes } from "@/router/routes";

import checkAccess from "@/access/checkAccess";

router.beforeEach(async (to, from, next) => {

let loginUser = store.state.user.loginUser;

// 如果之前没有登录过,或者之前登录过可以进行自动登录

if (!loginUser.userRole) {

await store.dispatch("user/getLoginUser");

loginUser = store.state.user.loginUser;

}

// 如果这个页面不需要访问权限

const needAccess = (to?.meta?.access as string) ?? ACCESS_ENUM.NOT_LOGIN;

if (needAccess !== ACCESS_ENUM.NOT_LOGIN) {

if (

loginUser.userRole === ACCESS_ENUM.NOT_LOGIN ||

!loginUser ||

!loginUser.userRole

) {

next(`/user/login?redirect=${ to.fullPath}`);

return;

}

if (!checkAccess(loginUser, needAccess)) {

next("/noAuth");

return;

}

}

next();

});

上述的代码可以直接进行复制,只需要loginUser中所定义的属性需要和我的相同,也就是userRole

上诉代码编写完之后,直接在main.ts中引入相关代码即可

import "@/access";

监听页面中的某个变量是否有改变

使用watch函数来进行监听,他所监听的变量是props.language,只要这个变量变了,他就会执行下面的操作。

watch(

() => props.language,

() => {

monaco.editor.setModelLanguage(

// 踩坑一定要使用toRaw

toRaw(codeEditor.value).getModel(),

props.language

);

}

);

监听好几个变量是否有改变所做的操作

只要loadData函数中所设计到的其中一个变量改变了,就会执行这个操作

watchEffect(() => {

loadData();

});

需要Dom元素加载完后才执行的逻辑

在前端开发中经常见道需要某个dom元素加载完之后才可以执行某些逻辑的情况,在这里我们可以利用onMounted的钩子函数。

import { defineProps, onMounted, ref, toRaw, watch, withDefaults } from "vue";

然后我们假设我们需要等下面这个div加载之后才执行之后的逻辑:

<template>

<div id="code-editor" ref="codeEditorRef" style="min-height: 700px"></div>code>

</template>

我们使用ref获取这个div的实例化对象

const codeEditorRef = ref();

然后再使用onMounted,进行监听

onMounted(() => {

if (!codeEditorRef.value) {

return;

}

// 执行之后的操作..

});


React相关内容总结

ProComponents - ProCompnents GitHubAntDesign - AntDesign组件文档

最佳实践

AntDesignPro-Preview-Vue - AntDesignPro-Vue3+Ts版本Preview

image-20240716180232677

AntDesignPro-Preview-React - AntDesignPro-React版本Preview

image-20240716180251028

React相关开发语法

定义页面变量

比如说我们页面需要有三个变量<code>basicInfomodelConfigfileConfig这种,在vue3中我们会使用ref语法,但是在react中不一样,会使用下面这种方式:

const [basicInfo, setBasicInfo] = useState<API.GeneratorEditRequest>();

const [modelConfig, setModelConfig] = useState<API.ModelConfig>();

const [fileConfig, setFileConfig] = useState<API.FileConfig>();

其中setxxxx为为某个变量赋值,比如下面这种方式:

onFinish={async (values) => {

setBasicInfo(values);

return true;

}}

定义组件

在react中定义组件也是十分重要的,这非常有利于简化代码的复杂程度。

比如以下这个界面:

image-20240716181750218

我们要将表格中最右边<code>修改创建按钮改为弹出框,并且作为组件的形式,减少主页面的代码复杂程度。比如创建,我们在新建文件,

image-20240716182005611

然后在主页面中进行引入,这里定义了几个值,首先是<code>createModalVisible,来控制这个弹出框是否可见,columns表单字段信息,onSubmit执行结束之后我们需要做什么事情,onCancel需要之后需要做什么事情:

<CreateModal

visible={createModalVisible}

columns={columns}

onSubmit={() => {

setCreateModalVisible(false);

actionRef.current?.reload();

}}

onCancel={() => {

setCreateModalVisible(false);

}}

/>

然后我们在CreateModel.tsx页面中定义对应的属性

interface Props {

visible: boolean;

columns: ProColumns<API.Generator>[];

onSubmit: (values: API.GeneratorAddRequest) => void;

onCancel: () => void;

}

定义好了之后,我们需要在主函数中对他进行使用

const { visible, columns, onSubmit, onCancel } = props;

整体效果:

/**

* 创建弹窗

* @param props

* @constructor

*/

const CreateModal: React.FC<Props> = (props) => {

const { visible, columns, onSubmit, onCancel } = props;

return (

<Modal

destroyOnClose

title={'创建'}

open={visible}

footer={null}

onCancel={() => {

onCancel?.(); // 这里使用onCancel?.(),含义为要是主页面传了这个函数才进行执行

}}

>

<ProTable

type="form"code>

columns={columns}

onSubmit={async (values: API.GeneratorAddRequest) => { // 这个是ProTable组件的onSubmit

const success = await handleAdd(values);

if (success) {

onSubmit?.(values); // 这个是调用我们父页面的onSubmit函数,是不一样的。

}

}}

/>

</Modal>

);

};

export default CreateModal;

监听变量是否有改变所做的操作

useEffect(() => {

if (!id) {

return;

}

loadData();

}, [id]);

检查id是否存在:效果函数开始时检查id是否存在(即id不是nullundefined或任何假值)。如果id不存在,函数直接返回并不执行loadData()。这可以防止在没有有效id时进行不必要的数据加载。

条件执行loadData():只有当id存在时,才调用loadData()。这通常用于根据id从服务器加载或请求数据。

依赖于id的执行:通过将id包含在依赖数组中,你告诉React,只有当id的值改变时才需要重新执行useEffect中的函数。这意味着:

如果id在重新渲染间保持不变,useEffect不会再次执行。如果id改变,无论是从undefined变为具体值,还是从一个具体值变为另一个具体值,useEffect都会执行。


至此,作为后端工程师来说,上面的内容应该足够开发大部分系统了。

由于喜欢React的代码风格,我编写了一套基于antd pro框架的一套模板,[yxinmiralce-antd-frontend-init] - 在Github上初步发布,里面包含了基础的CURD,OpenAPI相关配置,少些各种造轮子的代码。

如果有兴趣使用可以往下接着看,下面是我这个模板的ReadMe

YxinMiracle前端模板

一个属于后端工程师的前端极简模板。

基于 React + Ant Design 的项目初始模板,整合了常用框架和主流业务的示例代码。

[!IMPORTANT]

作者:YxinMIiracle

仅分享于 GitHub

本项目基于AntDesignPro-Preview-React进行二次开发,无任何商业用途!!

文章目录

Vue相关内容总结最佳实践Vue相关开发语法组件的编写权限校验监听页面中的某个变量是否有改变监听好几个变量是否有改变所做的操作需要Dom元素加载完后才执行的逻辑

React相关内容总结最佳实践React相关开发语法定义页面变量定义组件监听变量是否有改变所做的操作

✈ 版本要求 & 特性📦 安装教程🛰 相关特性1. 权限控制2. 权限赋予3. OpenAPI配置4. 动态主题变化5. 全局配置悬浮

🚄 更改项目名字🛣️ 相关文档

✈ 版本要求 & 特性

[!NOTE]

最好严格根据版本要求进行使用

node >= 16npm >= 7.20.6

📦 安装教程

将代码拉取到本地

<code>git clone https://github.com/YxinMiracle/yxinmiralce-antd-frontend-init.git

安装相关依赖

npm install

运行

npm run start:dev

🛰 相关特性

1. 权限控制

通过config/routes.ts路由中access的属性,来判断该试图是否可被这个用户所访问

export default [

{ path: '/welcome', icon: 'smile', component: './Welcome', name: '欢迎页' },

{

path: '/admin',

icon: 'crown',

name: '管理页',

access: 'canAdmin',

routes: [

{ path: '/admin', redirect: '/admin/user' },

{ icon: 'table', path: '/admin/user', component: './Admin/User', name: '用户管理' },

],

},

...

];

2. 权限赋予

在项目中src/access.ts的文件中,设置页面力度的权限设置

export default function access(initialState: { currentUser?: API.LoginUserVO } | undefined) {

const { currentUser } = initialState ?? { };

return {

canUser: currentUser, // routes.ts路由中要是配置了canUser,那么就执行这个逻辑

canAdmin: currentUser && currentUser.userRole === 'admin',

};

}

全局初始化对象initialState中的currentUser会在登录中所赋予,在注销中被消除,登录赋予逻辑在src/pages/User/Login/index.tsx中所赋予

const handleSubmit = async (values: API.UserLoginRequest) => {

try {

// 登录

const res = await userLoginUsingPost({

...values,

});

// 保存已登录用户信息

setInitialState({

...initialState,

currentUser: res.data,

});

const urlParams = new URL(window.location.href).searchParams;

history.push(urlParams.get('redirect') || '/');

return;

} catch (error: any) {

}

};

src/compoents/RightContent/AvatarDropdown.tsx的登出逻辑被消除

flushSync(() => {

setInitialState((s) => ({ ...s, currentUser: undefined }));

});

loginOut();

3. OpenAPI配置

config/config.ts中的OpenAPI配置更改自己的接口文档配置

openAPI: [

{

requestLibPath: "import { request } from '@umijs/max'",

schemaPath: 'http://localhost:8360/api/v2/api-docs',

projectName: 'backend',

},

],

4. 动态主题变化

官方Preview项目中并直接编写相关功能,作者根据Preview项目中的SettingDrawer逻辑进行二次开发

src/compoents/RightContent/AvatarDropdown.tsx中编写主题变化逻辑

构建开关

const switchTag = (

<Switch

checkedChildren="🌞"code>

unCheckedChildren="🌜"code>

onClick={switchDarkMode}

defaultChecked

></Switch>

);

设定开关逻辑

const switchDarkMode = async (checked: boolean) => {

const { settings, currentUser } = await getInitialState();

setInitialState(() => ({

settings: {

...settings,

navTheme: checked ? 'realDark' : 'light',

},

currentUser: currentUser,

}));

};

效果:

5. 全局配置悬浮

在文件<code>app.tsx中开启,可动态调整页面布局

childrenRender: (children) => {

// if (initialState?.loading) return <PageLoading />;

return (

<>

{children}

{isDev && (

<SettingDrawer

disableUrlParams

enableDarkTheme

settings={initialState?.settings}

onSettingChange={(settings) => {

setInitialState((preInitialState) => ({

...preInitialState,

settings,

}));

}}

/>

)}

</>

);

},

🚄 更改项目名字

作为喜欢从零到一的专业开发者,项目名字绝对是要自己起的,但是怎么去改呢?

git下来项目之后,先不打开项目,根据下面步骤执行

第一步:先把<code>yxinmiralce-antd-frontend-init替换成自己的项目名字。第二步:打开项目全局搜索shift+f6yxinmiralce-antd-frontend-init全部替换成项目名字。第三步:把文件夹下的.idea文件删除,重新打开ide即可获得自己的全新项目。

🛣️ 相关文档

ProComponents - ProCompnents GitHub

AntDesignPro-Preview-Vue - AntDesignPro-Vue3+Ts版本Preview

AntDesignPro-Preview-React - AntDesignPro-React版本Preview

AntDesign - AntDesign组件文档



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。