[关闭]
@Secretmm 2021-02-18T02:54:59.000000Z 字数 5706 阅读 514

前端工程化梳理

梳理


一:确定命名规范

命名规范:通过大家讨论之后形成文档,新项目和重构的项目必须遵守新命名规范

1.1 建议:

变量:小驼峰命名
文件夹:小驼峰命名
.ts文件:小驼峰命名
.vue文件:大驼峰命名【遵循自动生成规则】
图片: 小写字母,下划线连接
css类名:小写字母,短横杠连接
路由path:小写字母,短横杠连接
iconfont Font Family:iconfont_${项目名}
iconfont icon前缀:icon_${项目名}
iconfont icon:小写字母,短横杠连接

1.2 eslint

统一缩进、统一分号、统一空格的位置、统一对象尾部属性后的逗号等等

二:后端支持

2.1 确定后端接口返回code

需要与后端确定,确定之后,形成文档;

flag问题:需要保证:仅使用code码一定不会出错

2.2 字段类型确定

2.3 一些字段的固定格式

例如:时间只传秒级时间戳

三:确定统一的snippets

3.1 仅供参考:

3.1.1 Example.vue

  1. <template>
  2. <div>
  3. this is Example page
  4. </div>
  5. </template>
  6. <style scoped>
  7. </style>
  8. <script lang='ts'>
  9. import {
  10. Component,
  11. Emit,
  12. Inject,
  13. Model,
  14. Prop,
  15. Provide,
  16. Vue,
  17. Watch
  18. } from 'vue-property-decorator';
  19. import { State, Getter, Action, Mutation, namespace } from 'vuex-class';
  20. /* component组件 */
  21. @Component({
  22. components: {}
  23. })
  24. export default class Example extends Vue {
  25. /* @Inject/@Prop */
  26. /* data数据 /@Provide */
  27. /* method/@Emit/@Action/@Mutation */
  28. /* computed/@State/@Getter */
  29. /* Watch监听 */
  30. /* @Watch('xxx') */
  31. /* @Watch('xxx', { deep: true }) */
  32. /* lifecycle 生命周期 */
  33. mounted() {}
  34. }
  35. </script>

3.1.2 Example.tsx: hook

  1. import React, { useState, useEffect, Props } from 'react';
  2. import './Example.css';
  3. type ExampleProps = {
  4. }
  5. Example.defaultProps = {}
  6. function Example(props: ExampleProps & Props<any>) {
  7. //const [count, setCount] = useState(0);
  8. // Similar to componentDidMount and componentDidUpdate:
  9. //useEffect(() => {
  10. //dom更新后的操作
  11. //卸载组件的操作
  12. //return () => {}
  13. //},[count]);
  14. return (
  15. <div>this is Example</div>
  16. )
  17. }
  18. export default Example;

3.1.3 Example.tsx: class

  1. import React, { Props } from 'react';
  2. import './Example.css';
  3. import {Route, Switch, RouteChildrenProps } from 'react-router-dom';
  4. //import {} from '@/util/apiMethod';
  5. type ExampleProps = {
  6. }
  7. type ExampleState = {
  8. }
  9. class Example extends React.Component<ExampleProps & Props<any>, ExampleState> {
  10. constructor(props: ExampleProps & Props<any>) {
  11. super(props);
  12. this.state = {
  13. //count: 0
  14. };
  15. }
  16. static defaultProps:ExampleProps = {
  17. }
  18. render() {
  19. return (
  20. <div>this is Example</div>
  21. );
  22. }
  23. }
  24. export default Example;

四:项目脚手架

4.1 框架配置

4.2 src配置

第一部分:

  • main.ts:
  • router.ts:
  • App.vue:
  • 基本类型声明【.d.ts】:图片

第二部分:

  • views文件夹:仅用于路由界面,层级参考路由层级,可自动生成
  • components文件夹:
  • store文件夹:

第三部分:

  • interface文件夹:用于接口声明【index.ts用于公共接口声明】
  • util文件夹:index.ts用于公用方法声明;apiMethod.ts:自动生成的api方法引用
  • mixins文件夹:分发vue组件中的可复用功能
  • api文件夹:自动生成。存放后端接口相关;【详细见最后】

第四部分:

  • theme文件夹:【只放样式类】reset.css用于清除浏览器默认样式;index.css作为css访问入口
  • assets 文件夹:只放图片
  • importImage文件夹: 图片引用

4.3 其它

src结构选配,也可以通过脚手架相关命令在开发过程中添加;

五:工具集合

可通过npm包的形式

5.1 命令行工具

5.1.1 生成接口引用

见第六部分

5.1.2 生成图片引用

效果:输入命名行例如daddylab-tool create imgFile,根据assets目录中图片文件夹生成同名ts文件,ts文件中有对应的文件夹中图片的引用

ts文件示例

  1. import light1 from '../assets/activity/light1.png';
  2. import light2 from '../assets/activity/light2.png';
  3. import light3 from '../assets/activity/light3.png';
  4. import light4 from '../assets/activity/light4.png';
  5. import light5 from '../assets/activity/light5.png';
  6. import light6 from '../assets/activity/light6.png';
  7. export {
  8. light1,light2,light3,light4,light5,light6
  9. }

5.1.3 生成路由文件

vue文件举例,生成路由文件效果:输入命令如: daddylab-tool create --route Login,则在views目录中自动生成Login.vue,在route.ts中在对应层级加入基本的相关路由配置,例如:

  1. {
  2. path: '/login'
  3. }

考虑到路由配置的复杂度,这个命令行的主要功能还是为了使views文件夹和route.ts的目录结构变清晰,route.ts的其它配置暂时不考虑自动化

5.1.4 生成组件

生成组件效果:要生成一个在路由页面Main里使用的组件MainLayOut,输入命令例如daddylab-tool create --components /Main MainLayOut vue|react,则在components目录里自动就生成Main目录,里面有一个MainLayOut.vue|.tsx,并自动根据snippets生成好初始化代码

5.2 方法工具【npm包】

  • daddy-lib:一些常用方法,例如时间转换,localstorage,接口声明方法,等等等等,等待讨论
  • daddy-css:一些常用样式可以放入,用于组件库的样式统一

5.3 其它

  • 将工具尽可能的集成到脚手架中,升级脚手架

六:api结构相关

以:/api/class-studyreport/fictitious-way为例:
1.@/util/apiMethod:接口引用

  1. import {
  2. metaProvider as fictitiousWayProvider,
  3. Params as FictitiousWayParams,
  4. Response as FictitiousWayResponse
  5. } from '@/api/class-studyreport/fictitious-way';
  6. getFictitiousWay(params: FictitiousWayParams): Promise<FictitiousWayResponse> {
  7. return new Promise((resolve, reject) => {
  8. fetchApi(fictitiousWayProvider, params)
  9. .then(data => {
  10. resolve(data);
  11. })
  12. .catch(e => {
  13. reject();
  14. });
  15. });
  16. },

2.@/api/class-studyreport/fictitious-way: 接口声明

  1. import { ApiMetaProvider } from '@/api';
  2. const method = 'GET';
  3. const url = '/api/class-studyreport/fictitious-way';
  4. interface Params {
  5. id: number;
  6. startTime: number;
  7. studentUserId: string;
  8. teacherUserId: string;
  9. subject: string;
  10. category: number;
  11. title: string;
  12. }
  13. interface Response {
  14. id: number;
  15. subject: string;
  16. student_user_id: string;
  17. teacher_user_id: string;
  18. title: string;
  19. category: number;
  20. start_time: number;
  21. }
  22. const metaProvider: ApiMetaProvider<Params, Response> = function() {
  23. return {
  24. url: url,
  25. method: method
  26. };
  27. };
  28. export { metaProvider, Params, Response };

3.@/util/apiFetcher:fetchApi:接口统一处理

  1. import axios from 'axios';
  2. import qs from 'qs';
  3. import { ApiMetaProvider } from '@/api';
  4. import { transCamel } from 'xxxxx';
  5. import { Message } from 'xxxx';
  6. import Vue from 'vue';
  7. export const fetchProgress = Vue.observable({ percent: 0 });
  8. function onProgress(event: ProgressEvent) {
  9. fetchProgress.percent = Math.round((event.loaded * 100) / event.total);
  10. }
  11. export default async function fetchApi<P, R>(
  12. apiMetaProvider: ApiMetaProvider<P, R>,
  13. params?: P
  14. ): Promise<R> {
  15. try {
  16. const meta = apiMetaProvider();
  17. const { data } = await axios({
  18. url: meta.url,
  19. method: meta.method,
  20. headers: { 'X-Requested-With': 'XMLHttpRequest', 'Cache-Control': 'no-cache' },
  21. [meta.method === 'GET' ? 'params' : 'data']:
  22. meta.method === 'GET'
  23. ? transCamel(params || {})
  24. : qs.stringify(transCamel(params || {})),
  25. onDownloadProgress: onProgress
  26. });
  27. if (data.code === 200 || data.code === 0) {
  28. return data.data;
  29. } else {
  30. data.code !== 505 && Message.error(data.message);
  31. throw { code: data.code, message: data.message };
  32. }
  33. } finally {
  34. fetchProgress.percent = 0;
  35. }
  36. }
  37. export interface Api {
  38. fetch<P, R>(apiMetaProvider: ApiMetaProvider<P, R>, params: P): Promise<R>;
  39. }
  40. export interface FetchApi<P, R> {
  41. (apiMetaProvider: ApiMetaProvider<P, R>, params: P): Promise<R>;
  42. }

4.@/api:ApiMetaProvider

  1. interface ApiMeta {
  2. method: 'GET' | 'POST';
  3. url: string;
  4. }
  5. export interface ApiMetaProvider<P, R> {
  6. (): ApiMeta;
  7. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注