react笔记(二):header导航栏组件
新建脚手架
shell
npx create-react-app my-app --template typescript
npx create-react-app my-app --template typescript
运行以上命令会自动生成集成typescript的react脚手架
我也是第一次用typescript,现在的感受是:还是用js爽
刚刚写了一个header标题栏组件,因为不熟悉typescript,所以基本一直在坑里待着,刚刚才出来
这是刚刚写的标题栏,还没有加菜单点击展开和收起的效果
现在先讲正事
标题栏分了2个组件
第一个是header组件
用typescript的接口方式定义了props的参数类型
第12行开始就是
然后把接口当做props的类型,23行props:HeaderProps
至于这个接口默认值,原本觉得应该和文档一样,后面跟等号赋值,结果获取不到,最后还是利用老方法,在函数里另外设置默认值
js
import React from 'react'
import './open_header.css'
/**
* 使用interface定义props类型
* @param height:number 元素的高
* @param bagColor:string 元素的背景色
* @param child:React.ReactNode 标识子元素
* @param:? 问号代表不是必需,可不传参
*/
interface HeaderProps {
height?: number,//number类型
bagColor?: string,//字符串类型
borColor?: string,//字符串类型
children?: React.ReactNode,//节点类型
}
/**
* header布局头部元素
* @param props传参的数据 类型为HeaderProps
*/
function Header (props:HeaderProps) {
// 设置默认值
const height = props.height || 55;
const bagColor = props.bagColor || '#fff';
const borColor = props.borColor || '#fff';
return (
<div className="header" style={{height:`${height}px`,background:`${bagColor}`,borderBottom:`1px ${borColor} solid`}}>
{props.children}
</div>
);
}
export default Header;
import React from 'react'
import './open_header.css'
/**
* 使用interface定义props类型
* @param height:number 元素的高
* @param bagColor:string 元素的背景色
* @param child:React.ReactNode 标识子元素
* @param:? 问号代表不是必需,可不传参
*/
interface HeaderProps {
height?: number,//number类型
bagColor?: string,//字符串类型
borColor?: string,//字符串类型
children?: React.ReactNode,//节点类型
}
/**
* header布局头部元素
* @param props传参的数据 类型为HeaderProps
*/
function Header (props:HeaderProps) {
// 设置默认值
const height = props.height || 55;
const bagColor = props.bagColor || '#fff';
const borColor = props.borColor || '#fff';
return (
<div className="header" style={{height:`${height}px`,background:`${bagColor}`,borderBottom:`1px ${borColor} solid`}}>
{props.children}
</div>
);
}
export default Header;
header组件props参数主要用来实现:自定义高度,背景色和底部边框颜色,还有子组件 children。类似vue的插槽slot
下面实现navbar组件
js
import React from 'react'
import * as ReactDOM from 'react-dom';
//自定义图片组件,可自定义加载占位图和加载失败图
import Image from './image'
import './navbar.css'
/**
* 使用interface定义props类型
* @param logo: string 标题栏logo
* @param title: string 标题栏网站名
* @param data: any[] 菜单栏数据,类型是任意的数组类型
*/
interface NavbarProps {
logo?:string,
title?:string,
data?: any[],
top: numbar}
/**
* 菜单栏的默认数据
* :any[] 定义类型为任意数组类型
*/
var datas:any[] = [
{
title: '首页',
link: '/'
},
{
title: '产品',
link: '/'
},
{
title: '哈哈',
childs: [
{
title: '首页',
link: '/'
},
{
title: '产品',
link: '/'
}
]
},
]
/**
* navbar组件
* @param any,OpenNavbarProps类型
*/
class Navbar extends React.Component <any,NavbarProps> {
//props后需要定义类型
constructor(props:NavbarProps) {
super(props);
//获取props参数并设置默认值
this.state = {
logo: this.props.logo || require('../../assets/img/open.png'),
title: this.props.title || 'ui-react',
data: this.props.data || datas,
top: 0
}
}
componentDidMount() {
//获取当前navbar的高度,然后设置展开菜单距离顶部距离
// 类型断言,需要手动指定值的类型
let el = this.refs.navbar as HTMLElement;
this.setState({top: el.offsetHeight});
}
render() {
//需要判断一下菜单栏数据,因为是用的本地生命的数据,所以确定有,但是一直报错,判断一下后正常显示了
const data = this.state.data ? this.state.data : [];
return <div className="navbar" ref="navbar">
<div className="navbar-brand">
<Image width={40} height={40} ></Image>
<p className="nav-brand-title">{this.state.title}</p>
</div>
<ul className="navbar-nav">
//遍历时也要定义一下数据类型,因为之前定义的是数组类型,
// 但是这里循环内部数据是其他类型,所以要重新定义一下,不然会报错
// 定义any任意类型
{data.map((item:any,i)=>{
return <li key={i}>
{item.childs
? <div>
<p>{item.title}</p>
<ul style={{top:`${this.state.top}px`}}>
//这里也一样,遍历都重新定义一下类型,这个坑我花了大半个小时才填好
// 一直以为是上面定义菜单数据 时候类型不对,一直去看typescript文档
// 希望找出原因,但是档档最多就定义了2层类型,里面没法定义
// 后面才发现是这里要重新定义一下即可,
{item.childs.map((items:any,index:any)=>{
return <li key={index} >{items.title}</li>
})}
</ul>
</div>
: <div><p>{item.title}</p></div>
}
</li>
})}
</ul>
</div>
}
}
export default Navbar;
import React from 'react'
import * as ReactDOM from 'react-dom';
//自定义图片组件,可自定义加载占位图和加载失败图
import Image from './image'
import './navbar.css'
/**
* 使用interface定义props类型
* @param logo: string 标题栏logo
* @param title: string 标题栏网站名
* @param data: any[] 菜单栏数据,类型是任意的数组类型
*/
interface NavbarProps {
logo?:string,
title?:string,
data?: any[],
top: numbar}
/**
* 菜单栏的默认数据
* :any[] 定义类型为任意数组类型
*/
var datas:any[] = [
{
title: '首页',
link: '/'
},
{
title: '产品',
link: '/'
},
{
title: '哈哈',
childs: [
{
title: '首页',
link: '/'
},
{
title: '产品',
link: '/'
}
]
},
]
/**
* navbar组件
* @param any,OpenNavbarProps类型
*/
class Navbar extends React.Component <any,NavbarProps> {
//props后需要定义类型
constructor(props:NavbarProps) {
super(props);
//获取props参数并设置默认值
this.state = {
logo: this.props.logo || require('../../assets/img/open.png'),
title: this.props.title || 'ui-react',
data: this.props.data || datas,
top: 0
}
}
componentDidMount() {
//获取当前navbar的高度,然后设置展开菜单距离顶部距离
// 类型断言,需要手动指定值的类型
let el = this.refs.navbar as HTMLElement;
this.setState({top: el.offsetHeight});
}
render() {
//需要判断一下菜单栏数据,因为是用的本地生命的数据,所以确定有,但是一直报错,判断一下后正常显示了
const data = this.state.data ? this.state.data : [];
return <div className="navbar" ref="navbar">
<div className="navbar-brand">
<Image width={40} height={40} ></Image>
<p className="nav-brand-title">{this.state.title}</p>
</div>
<ul className="navbar-nav">
//遍历时也要定义一下数据类型,因为之前定义的是数组类型,
// 但是这里循环内部数据是其他类型,所以要重新定义一下,不然会报错
// 定义any任意类型
{data.map((item:any,i)=>{
return <li key={i}>
{item.childs
? <div>
<p>{item.title}</p>
<ul style={{top:`${this.state.top}px`}}>
//这里也一样,遍历都重新定义一下类型,这个坑我花了大半个小时才填好
// 一直以为是上面定义菜单数据 时候类型不对,一直去看typescript文档
// 希望找出原因,但是档档最多就定义了2层类型,里面没法定义
// 后面才发现是这里要重新定义一下即可,
{item.childs.map((items:any,index:any)=>{
return <li key={index} >{items.title}</li>
})}
</ul>
</div>
: <div><p>{item.title}</p></div>
}
</li>
})}
</ul>
</div>
}
}
export default Navbar;
然后就是引用
html
<Header height={55} borColor={'#eee'}>
<Navbar title={"openui"}/>
</Header>
<Header height={55} borColor={'#eee'}>
<Navbar title={"openui"}/>
</Header>
header组件引用navbar,
因为第一次用typescript,可能有些地方还没有写好,后续出问题再修改吧!
本文到此结束。
反馈信息
INFO