第一章 本周导学
1-1 本章介绍
- 组件平台
- 组件预览
- 组件README
第二章 组件平台架构设计和技术选型
2-1 组件平台架构设计
2-2 组件平台技术选型和框架搭建
umi-component-test改项目代码提交至:https://github.com/liugezhou/umi-component-test
- 初始化UmiJS
- mkdir umi-component-dev
- cd umi-component-dev
- yarn create @umijs/umi-app
- yarn install
- yarn start
2. 新建页面
npx umi g page detail
- 配置路由
.umirc.ts新建页面路由 routes: [ { path:’/‘, component:’@/pages/index’ }, { path:’/nice’, component:’@/pages/detail’ }, ], yarn start启动项目后,访问 http://192.168.1.3:8000/nice即可看到最新的页面
4. 安装 Ant Design
yarn add antd
- 使用 ```html import { Button } from “antd”;
<a name="mbeWU"></a>###<a name="JWwJv"></a>### 第三章 组件平台基础功能开发---<a name="CmlG4"></a>#### <br />3-1 umi 项目全局入口文件+国际化开发> - 运行时配置:约定src/app.tsx为运行时配置,该配置文件下可以做一些全局性的操作> - 国际化:文档-插件-@umijs/plugin-locale> - **mkdir:**src/locales/zh-CN.ts | en-US.ts,配置WELCOME_TO_UMI_WORLD字段内容> - .umirc.ts中配置国际化[代码如下所示]> - src/app.tsx中引入国际化[代码如下所示]```javascript// src/umirc.tslocale: {default: 'zh-CN',antd: false,title: false,baseNavigator: true,baseSeparator: '-',},// src/app.tsximport 'antd/dist/antd.css';import { getLocale,setLocale } from 'umi';import qs from 'qs';const { search } = window.location;const { locale = 'zh-CN' } = qs.parse(search, { ignoreQueryPrefix: true });setLocale(locale,false)// src/pages/index.tsximport { useIntl } from 'umi';export default function IndexPage() {const init = useIntl()const msg = init.formatMessage({id:'WELCOME_TO_UMI_WORLD',defaultMessage:'你好,牛逼的前端架构师!'},{name:'liugezhou'})console.log(msg)}
最后:yarn start启动文件,访问查看控制台:http://localhost:8000/?locale=en-US
3-2 组件平台功能展示 + 页头页脚开发
umijs支持layout引入,于是我们在开发页头页脚的时候,页面页头与页脚是在各个页面都存在的,于是我们可以将页面不同的地方以layout的形式注入
// src/layouts/index.jsimport styles from './index.less'function BasicLayout(props){return (<div className={styles.normal}><div className={styles.title}>{slogan}</div>{props.children}<div className={styles.footer}>{copyright}</div></div>)}export default BasicLayout//src/layouts/index.less@import '~antd/dist/antd';.normal{text-align:center}.title{font-size:15px;font-weight: normal;background: @primary-color;padding: 10px 0;color: white;margin:0;}.footer {font-size: 12px;font-weight: normal;background: @primary-color;padding: 10px 0;color: white;margin: 0;}// .umijsrc.jsimport { defineConfig } from 'umi';export default defineConfig({nodeModulesTransform: {type: 'none',},routes: [{path: '/',component: '@/layouts/index',routes: [{path: '/',component: '@/pages/index'},{path: '/detail',component: '@/pages/detail'}]}],fastRefresh: {},locale: {default: 'zh-CN',antd: false,title: false,baseNavigator: true,baseSeparator: '-',}});
3-3 组件平台动态配置 API 开发
我们的页面与页脚内容需要从接口获取,因此,本节内容为在 cloudscope-cli-server服务中去编写接口代码。 本周相关代码提交至:cloudscope-cli/server/lesson33
3-3-1 添加路由
// app/route.js'use strict';module.exports = app => {const { router, controller } = app;router.resources('componentSite', '/api/v1/componentSite', controller.v1.componentSite);};
3-3-2 API编写
// app/controller/v1/ComponentSite.js'use strict'const Controller = require('egg').Controller;const mongo = require('../../utils/mongo')class ComponentSiteController extends Controller{asynx index(){const { ctx } = this;const data = await mongo.query('componentSite);if(data && data.length>0){ctx.body = data[0]}else{ctx.body = []}}}
- 需要在mongoDB中新建集合 componentSite,添加页面页脚数据
- 启动项目,浏览器输入地址,测试访问结果
3-4 前端动态配置 API 接入
代码无分支提交至:umi-component-test
上一节我们开发完毕api接口后,在前端请求该接口
- 首先需要install axios
- 接着需要封装request请求。
// src/layouts/util/reques.jsimport axios from 'axios';const BASE_URl = 'http://liugezhou.com:7001'const service = axios.create({baseURl:BASE_URL,timeout:5000})service.interceptor.request.use({config => {return config;}error => {return new Promise.reject(error)}service.interceptor.response.use({response => {return new response.data;}error => {return new Promise.reject(error)}})export default request;//src/layouts/utils/service.jsimport request from './request'export function getSiteInfo(){return request({url:'/api/v1/componentSite'})}export default {}
- 最后,在首页中去调用该接口,且赋值为页头与页脚
// src/layouts/index.jsimport styles from './index.less';import {getSiteInfo } from '../utils/service';import { useState, useEffect } from 'react'';function BasicLayout(props){const [init,setInit] = useState(false);const [slogan,setSlogan] = useState('');const [copyright,setCopyright ] = useState('');useEffect( () =>{if (!init) {setInit(true);getSiteInfo().then(data => {setSlogan(data.slogan);setCopyright(data.copyright);}).catch(err => {console.log(err);setSlogan('');setCopyright('');});}},[init])return (<div className={styles.normal}><div className={styles.title}>{slogan}</div>{props.children}<div className={styles.footer}>{copyright}</div></div>)}export default BasiLayout;
第四章 组件平台组件列表页面开发
4-1 组件列表 API 开发
本周相关代码提交至:cloudscope-cli/server/lesson33
// app/controller/v1/Components.jsasync index(){const { ctx, app } = this;const { name } = ctx.query;const andWhere = name ? `AND c.name LIKE '%${name}%'` : '';const sql = `SELECT c.id, c.name, c.classname, c.description, c.npm_name, c.npm_version, c.git_type, c.git_remote, c.git_owner, c.git_login, c.create_dt, c.update_dt, v.version, v.build_path, v.example_path, v.example_listFROM component AS cLEFT JOIN version AS v ON c.id = v.component_idWHERE c.status = 1 AND v.status = 1 ${andWhere}ORDER BY c.create_dt, v.version DESC`;const result = await app.mysql.query(sql);const components = [];result.forEach(component => {let hasComponent = components.find(item => item.id === component.id);if (!hasComponent) {hasComponent = {...component,};delete hasComponent.version;delete hasComponent.build_path;delete hasComponent.example_path;delete hasComponent.example_list;hasComponent.versions = [];components.push(hasComponent);hasComponent.versions.push({version: component.version,build_path: component.build_path,example_path: component.example_path,example_list: component.example_list,});} else {hasComponent.versions.push({version: component.version,build_path: component.build_path,example_path: component.example_path,example_list: component.example_list,});}});ctx.body = components;}
4-2 测试组件数据创建
- 上一节我们获取的组件列表数据为一条,本节首先再去创建几条测试数据。
在此之前,先去更改之前的组件模版 cloudscope-cli-components,packahe.json的配置:publishConfig为access:true 和 build:demo
- 外层package.json 增加一个版本号
- template内package.json 配置 publishConfig
- template内package.json 配置‘build:demo’:”npm install && npm run build && cd examples && npm install && npm run build”
- npm publish
- mongodb数据库中将版本号升级
mkdir cloudscope-cli_component-test3
- cd cloudscope-cli_component-test3
- cloudscope-cli init -tp /Users/liumingzhou/Documents/imoocCourse/Web前端架构师/cloudscope-cli/commands/init/ 【这里需要注意的是,由于我本读安装且默认设置了node的版本为14,而之前开发的本地脚手架为12.16,因此支持以上代码需要更换node版本】
- @cloudscope-cli/components-test2
- 1.0.0
- 通用的Vue3组件库模版
- components test2
- code .
- npm run build:demo
- cloudscope-cli publish -tp /Users/liumingzhou/Documents/imoocCourse/Web前端架构师/cloudscope-cli/commands/publish/ [不加prod属性,不用关心OSS与NPM发布]
检查:
- git remote -v 【查看远程仓库信息】
查看mysql数据库插入信息
如果添加了 —prod属性
- 查看npm发布组件信息
- 查看云OSS上传信息
4-3 组件列表页面开发
4-4 组件卡片面板开发
4-5 搜索框组件开发+模糊搜索API开发
这三节内容为组件首页列表的umi项目代码开发,包括布局、请求、点击事件等功能,代码分类为:国际化配置、工具类、业务代码,其中核心内容为业务代码,主要是使用UI库ant-design-react和umi以及react的一些用法。
- 国际化配置
// src/locales/en-US.jsexport default {WELCOME_TO_UMI_WORLD: "{name}, welcome to umi's world",INDEX_SEARCH: 'Search',INDEX_PLACEHOLDER: 'Component Name',};// src/locales/zh-CN.jsexport default {WELCOME_TO_UMI_WORLD: '{name},欢迎光临umi的世界',INDEX_SEARCH: '搜索',INDEX_PLACEHOLDER: '组件名称',};
- 工具类
// src/utils/index.jsexport function formatName(name) {let _name = name;if (_name && _name.startsWith('@') && _name.indexOf('/') > 0) {// @cloudscope-cli/component-test ->// @cloudscope-cli_component-testconst nameArray = _name.split('/');_name = nameArray.join('_').replace('@', '');}return _name;}export default {};// src/utils/request.jsimport axios from 'axios'const BASE_URL = 'http://liugezhou.com:7001'const service = axios.create({baseURL: BASE_URL,timeout: 5000})service.interceptors.request.use(config => {return config},error => {return Promise.reject(error)})service.interceptors.response.use(response => {return response.data},error => {return Promise.reject(error)});export default service;//src/utils/service.jsimport request from './request';export function getSiteInfo() {return request({url: '/api/v1/componentSite',});}export function getComponentList(params) {return request({url: '/api/v1/components',params,});}export default {};//src/utils/git.jsimport { formatName } from '../utils';export function getGitUrl(item) {let name = item.classname;if (name.startsWith('@') && name.indexOf('/') > 0) {const nameArray = name.split('/');name = nameArray.join('_').replace('@', '');}return `https://${item.git_type}.com/${item.git_login}/${name}`;}/*** 获取组件预览链接* @param name 组件名称* @param version 组件版本* @param path 组件预览文件路径* @param file 组件预览文件名称*/export function getPreviewUrl({ name, version, path, file }) {// 上传至OSS再来配置return `https:// /${formatName(name)}@${version}/${path}/${file}`;}export default {};
- 业务代码 ✨✨✨✨✨
// src/pages/index.jsximport styles from './index.less';import { Divider, Row, Col, Card, Input } from 'antd';import { EditOutlined, EllipsisOutlined, EyeOutlined } from '@ant-design/icons';import { useIntl,history } from 'umi';import { getGitUrl,getPreviewUrl } from "../utils/git";import { getComponentList } from '../utils/service';import { useState, useEffect } from "react";const { Meta } = Card;const { Search } = Input;export default function IndexPage() {const [init, setInit] = useState(false);const [list, setList] = useState([]);const [name, setName] = useState(null);const intl = useIntl();useEffect(() => {if (!init) {setInit(true);getComponentList({ name }).then(data => {console.log(data);setList(data);}).catch(err => {console.error(err);setList([]);});}}, [init, name]);function getAvatar(item) {if (item.git_type === 'gitee') {return {img: 'https://gitee.com/static/images/logo-black.svg',style: { height: '20px', cursor: 'pointer' },};} else {return {img: 'https://www.youbaobao.xyz/arch/img/github.jpeg',style: { height: '40px', cursor: 'pointer' },};}}function getLastPreviewUrl(item) {const lastVersion = item.versions[0];const examplePath = lastVersion.example_path;const exampleFile = JSON.parse(lastVersion.example_list)[0];return getPreviewUrl({name: item.classname,version: lastVersion.version,path: examplePath,file: exampleFile,});}return (<div className={styles.container}><div className={styles.search}><Searchstyle={{ width: '50%' }}placeholder={intl.formatMessage({ id: 'INDEX_PLACEHOLDER' })}allowClearenterButton={intl.formatMessage({ id: 'INDEX_SEARCH' })}size='large'onSearch={value => {setName(value);setInit(false);}}/></div><Divider orientation='right'>共{list.length}个组件</Divider><Row gutter={16} justify='space-around'>{list.map(item => (<Col span={6} key={item.id}><Cardactions={[<EyeOutlined key='setting' onClick={() => {window.open(getLastPreviewUrl(item));}}/>,<EditOutlined key='edit' onClick={() => {history.push({pathname: '/detail',query: {id: item.id,},});}}/>,<EllipsisOutlined key='ellipsis' />,]}><Metaavatar={<imgalt={item.name}src={getAvatar(item).img}style={getAvatar(item).style}onClick={() => window.open(getGitUrl(item))}/>}title={item.name}description={item.description}/></Card></Col>))}</Row></div>);}// src/pages/index.less.containter {padding: 20px;}.search {padding:30px;}
第五章 组件平台组件详情页面开发
5-1 组件详情获取API开发
首先在umi-component-dev项目下写details页面
// src/pages/details.jsximport { useState, useEffect } from 'react';import styles from './detail.css';import { getComponentItem } from "../utils/service";export default function Page(props) {const [init, setInit] = useState(false)const [data, setData] = useState(null)useEffect(() => {const query = props.location.queryif(!init && query.id){setInit(true);getComponentItem(query.id).then( data=> {console.log(data)})}})return (<div></div>);}// src/utils/service.jsexport function getComponentItem(id) {return request({url: `/api/v1/components/${id}`,});}
接着,重点就是去开发接口获取组件的具体信息了
// app/controller/v1/Components.js// api/v1/components/:idasync show() {const { ctx, app } = this;const id = ctx.params.idconst results = await app.mysql.select('component', {where: { id }})if (results && results.length > 0) {const component = results[0]component.versions = await app.mysql.select('version', {where: { component_id: id },orders: [['version', 'desc']]})// gitee GET https://gitee.com/api/v5/repos/{owner}/{repo}/contents(/{path})// git GET https://api.github.com/repos/{owner}/{repo}/{path})let readmeUrl;let _name = component.classname;if (_name && _name.startsWith('@') && _name.indexOf('/') > 0) {const nameArray = _name.split('/');_name = nameArray.join('_').replace('@', '');}if (component.git_type === 'gitee') {readmeUrl = `https://gitee.com/api/v5/repos/${component.git_login}/${_name}/contents/README.md`} else {readmeUrl = `https://api.github.com/repos/${component.git_login}/${_name}/README.md`}const readme = await axios.get(readmeUrl);let content = readme.data && readme.data.content;if (content) {content = decode(content)if (content) {component.readme = content}}ctx.body = component} else {ctx.body = {}}}
