1、四角边框怎么实现
利用线性渐变(linear-gradient)去实现
div {
width: 500px;
height: 500px;
border: 1px solid red;
background:
/* 左上角向下 */
linear-gradient(to bottom,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) left top no-repeat,
/* 左上角向右 */
linear-gradient(to right,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) left top no-repeat,
/* 右上角向下 */
linear-gradient(to bottom,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) right top no-repeat,
/* 右上角向左 */
linear-gradient(to left,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) right top no-repeat,
/* 左下角向上 */
linear-gradient(to top,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) left bottom no-repeat,
/* 左下角向右 */
linear-gradient(to right,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) left bottom no-repeat,
/* 右下角向上 */
linear-gradient(to top,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) right bottom no-repeat,
/* 右下角向左 */
linear-gradient(to left,
rgb(89, 134, 223) 0px,
rgb(89, 134, 223) 5px,
transparent 0px,
transparent 100%) right bottom no-repeat;
/* 控制渐变背景的尺寸大小 */
background-size: 2rem 1rem;
}
2、宽度自适应内容
使用 fit-content 属性去实现
<style>
.news {
background-color: #334e70;
padding: 5px 8px;
color: #fff;
font-size: 14px;
border-radius: 5px;
font-weight: 500;
display: flex;
align-items: center;
/* 宽度适应内容 */
width: fit-content;
}
a {
font-size: 12px;
margin-left: 5px;
color: #9c9797;
transform: translateY(10%);
}
</style>
<div class='news'>
<span>热门话题</span>
<a>更多</a>
</div>
3、antd 中,对于表单数据的管理
使用form 去维护所有的表单数据,antd 底层维护表单数据其实也是通过rc-form 这个第三方库实现的
getFieldError: 获取对应字段名的错误信息
getFieldInstance: 获取对应字段实例
getFieldsError: 获取一组字段名对应的错误信息,返回为数组形式
getFieldsValue: 获取一组字段名对应的值,会按照对应结构返回。默认返回现存字段值,当调用 `getFieldsValue(true)` 时返回所有值
getFieldValue: 获取对应字段名的值
isFieldsTouched: 检查一组字段是否被用户操作过,`allTouched` 为 `true` 时检查是否所有字段都被操作过
isFieldTouched: 检查对应字段是否被用户操作过
isFieldValidating: 检查对应字段是否正在校验
resetFields: 重置一组字段到 `initialValues`
scrollToField: 滚动到对应字段位置
setFields: 设置一组字段状态,可以给对应的表单统一设置一组状态(value,error...)
setFieldValue: 设置对应表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入)
setFieldsValue: 设置一组表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入)。
如果你只想修改 Form.List 中单项值,请通过 `setFieldValue` 进行指定
submit: 提交表单,与点击 `submit` 按钮效果相同
validateFields: 触发表单验证
antd3 中如果需要使用表单之外非用户输入的状态数据,可以先使用getFieldDecorator 注册表单字段,
然后form 就会把该状态维护到表单里面了,就可以使用form 提供的相关api 去操作该字段状态
antd4+ 中则可以直接使用Form.Item 注册(<Form.Item name='nameKey' />),getFieldDecorator 在antd4 已经被废弃了,
注意Form.Item 需要在Form 里面进行注册(<Form form={form}><Form.Item name='nameKey' /></Form>)
4、antd 中使用密码框,解决浏览器会自动填充的问题
给input 添加该属性
autoComplete='off' / autoComplete='new-password'
5、react 中状态透传的小坑
场景:
子组件的某一状态是依赖于父组件透传下来的状态,父组件状态更新,子组件会重渲染(子组件原有状态会保持,不过组件会重新渲染一次);但是子组件初始化的状态是依据父组件传下的状态,重渲染的时候子组件的状态并没有被重新依据最新透传下来的状态进行初始化,而是依旧沿用第一次render 的数据(也就是组件原有的状态)
注意:重渲染,并不是组件重新初始化创建进行渲染,而是指组件重新更新进行渲染,组件原有的状态会依旧保持
解决:
使用useEffect 监听props,重新set 子组件状态
const Demo = (props: any) => {
const { data } = props;
const [demo, setDemo] = useState<any>(data);
useEffect(() => {
setDemo(data);
}, [data]);
return <div>{demo}</div>
}
// 注意,这种给组件默认值的情况,而且还是一个复杂类型的默认值,如果使用了useEffect 进行监听并去修改状态的话,不管data 是否改变,副作用都会被执行,因为副作用的依赖比较是使用Object.is() 进行比较的,是浅层比较,所以每次副作用的执行,他的依赖状态其实都是改变了的(引用已经改变了),可以JSON 化监听或者添加判断,避免不必要的执行
const Demo = (props: any) => {
const { data = {} } = props;
const [demo, setDemo] = useState<any>(data);
useEffect(() => {
if(xxx) return;
setDemo(data);
}, [JSON.stringify(data)]);
return <div>{demo}</div>
}
6、关于复杂类型数据的坑和妙用
问题
复杂类型数据就是对象,也就是引用数据。所谓引用数据,变量所存储的都是引用数据所指向的内存地址,存储的值都指向同一引用中的堆内数据。赋值操作并不是把值赋予了新变量,而是将引用地址赋予了新变量,指向的数据依旧是同一数据源;所以互相修改数据会存在引用污染,要杜绝这种情况就需要将数据进行拷贝,拷贝数据源,开辟新的堆内存进行存储。
场景
对于列表渲染的数据,我们需要对每一项数据进行修改:
- 利用引用关系,直接修改值(违背了数据不可变的原则,容易造成数据源混乱,引用注入;但是方便,在数据量很大,数据结构复杂的情况下,可以利用引用关系对数据进行操作)
- 利用每项数据唯一的标识,找到对应的数据,对数据进行拷贝修改,然后将数据进行整合,重新赋值,更新数据源(遵循数据的不可变原则,数据可读性好,来源清晰,不过操作繁琐,需要遍历数据进行操作)
- 有时候在复杂的表数据里面对数据进行操作,利用引用或许是一个不错的选择,但是不推荐,不过可以使用immer 对状态数据进行修改
- 注:Immer 总是会根据你对
draft的修改来从头开始构建下一个 state。这使得你的事件处理程序非常的简洁,同时也不会直接修改 state
// 1、引用关系
const App: React.FC<any> = () => {
const [data] = useState<any>([
{ name: '小明', age: 28 },
{ name: '小北', age: 20 },
{ name: '小钰', age: 18 },
{ name: '小红', age: 25 },
]);
const [_, render] = useState(false);
return data.map((item: any, index: number) => {
return (
<div key={index}>
<span>name: {item.name}</span>
<span
onClick={() => {
item.age++;
render(b => !b);
}}
>age: {item.age}</span>
</div>
);
})
}
// 2、遍历拷贝
const App: React.FC<any> = () => {
const [data, setData] = useState<any>([
{ name: '小明', age: 28 },
{ name: '小北', age: 20 },
{ name: '小钰', age: 18 },
{ name: '小红', age: 25 },
]);
return data.map((item: any, index: number) => {
return (
<div key={index}>
<span>name: {item.name}</span>
<span
onClick={() => {
const newData = data.map((i: any, idx: number) => {
if (index === idx) {
return { ...i, age: i.age + 1 };
}
return i;
});
setData(newData);
}}
>age: {item.age}</span>
</div>
);
})
}
// 3、使用immer
import { useState } from 'react';
import { useImmer } from 'use-immer';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [myList, updateMyList] = useImmer(initialList);
const [yourList, updateYourList] = useImmer(initialList);
function handleToggleMyList(id, nextSeen) {
updateMyList(draft => {
const artwork = draft.find(a =>
a.id === id
);
artwork.seen = nextSeen;
});
}
function handleToggleYourList(artworkId, nextSeen) {
updateYourList(draft => {
const artwork = draft.find(a =>
a.id === artworkId
);
artwork.seen = nextSeen;
});
}
return (
<>
<h1>艺术愿望清单</h1>
<h2>我想看的艺术清单:</h2>
<ItemList
artworks={myList}
onToggle={handleToggleMyList} />
<h2>你想看的艺术清单:</h2>
<ItemList
artworks={yourList}
onToggle={handleToggleYourList} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
快速剔除一个引用数据不必要的字段
利用解构 + 剩余参数
const data = { name: '你好', age: '666', sex: '男' };
const { sex, ...other } = data;
// other => { name: '你好', age: '666' }
other.age = 999;
// other => { name: '你好', age: 999 }
// data => { name: '你好', age: '666', sex: '男' }
关于引用关系的一个小坑
map、filter 等操作产生的新的数据,只是浅拷贝,直接修改深层数据,依旧会造成数据污染,影响到源数据
const data = [
{name: '小明', age: 18},
{name: '小北', age: 28},
];
const newData = data.map(i => i)
// data => [{name: '小明', age: 18}, {name: '小北', age: 28}]
// newData => [{name: '小明', age: 18}, {name: '小北', age: 28}]
newData.push({name: '小红', age: 30})
// data => [{name: '小明', age: 18}1: {name: '小北', age: 28}]
// newData => [{name: '小明', age: 18}, {name: '小北', age: 28}, {name: '小红', age: 30}]
newData[0].age = 99
// 影响了源数据data
// data => [{name: '小明', age: 99}, {name: '小北', age: 28}]
// newData => [{name: '小明', age: 99}, {name: '小北', age: 28}, {name: '小红', age: 30}]
7、画三角形
利用css3 的clip-path
-webkit-clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
8、关于antd 中的Input 组件
defaultValue 只认初始化渲染的数据,后续数据的改变,无法影响defaultValue
9、页面切换,重新进入页面触发事件(visibilitychange)
/** 跳转页面回来强制刷新 */
useEffect(() => {
document.addEventListener('visibilitychange', () => {
(document.visibilityState === 'visible') && setIsUpdate(i => !i)
})
return () => {
document.removeEventListener('visibilitychange', () => (''))
}
}, [])
10、对数值或者是字符串数值的取整操作
/**
* ~~
* 类似于parseInt 可以把数值、字符转化成整数
*/
console.log(~~'52.99') // 52
/**
* parseInt()
* 把数值、字符转化成整数
*/
console.log(Number.parseInt('52.99')) // 52
/**
* floor()
* 向下取整
*/
console.log(Math.floor('52.99')) // 52
11、PC端内嵌网页(iframe)
HTML 内联框架元素 (<iframe>) 表示嵌套的浏览内容,它能够将另一个 HTML 页面嵌入到当前页面中。
嵌套iframe 怎么获取顶层域的window 对象(window.top)
// 获取mk 系统中的全局变量里的一个状态标识
const platform = window.top ? (window.top as any)?.mk.getSysConfig('platform') : mk.getSysConfig('platform')
12、H5页面嵌套(WebView)
Android 内置webkit 内核的高性能浏览器,而WebView 则是在这个基础上进行封装后的一个控件,WebView 直译网页视图,我们可以简单的看作一个可以嵌套到界面上的一个浏览器控件!
通信方式
原生向H5 通信,可通过浏览器的url 进行query 传参;H5 向原生通信需要两边配合,使用postMessage 进行通信
13、表示颜色透明
- 透明度为0 => opacity: 0;
- 使用rgba => rgba(0, 0, 0, 0);
- 使用透明属性 => color: transparent;
14、react 中层级较深的组件数据透传
使用useContext() 和useReducer(),子孙组件向父级通信,采用上下文透传dispatch,避免层层回调
// 创建上下文对象
const TodosContext = React.createContext<any>(null)
/***************** 父组件 *******************/
const Counter = () => {
// const [state: 状态数据, dispatch: 派发action 的dispatch] = useReducer(reducer: reducer 函数, initialState: 初始化的state)
const [state, dispatch] = useReducer((preState: any, action: { type: string; payload: number }) => {
const { type, payload } = action
switch (type) {
case 'increment':
return { ...preState, count: preState.count + payload }
case 'decrement':
return { ...preState, count: preState.count - payload }
default:
return { ...preState }
}
}, { count: 10 } as any)
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement', payload: 5 })}>-</button>
<button onClick={() => dispatch({type: 'increment', payload: 15 })}>+</button>
<TodosContext.Provider value={dispatch}>
<DeepTree state={state} TodosContext={TodosContext} />
</TodosContext.Provider>
</>
);
}
/***************** 子组件 *******************/
interface IDispatch {
type: string
payload: number
}
const DeepTree: any = ({ state, TodosContext }) => {
const dispatch: (args: IDispatch) => void = useContext(TodosContext)
return (
<Fragment>
<h1>{state?.count}</h1>
<button onClick={() => dispatch({ type: 'increment', payload: 5 })}>+</button>
<button onClick={() => dispatch({ type: 'decrement', payload: 10 })}>-</button>
<button onClick={() => dispatch({ type: 'other' })}>其他</button>
</Fragment>
)
}
15、Sass 的使用
@import "~@elements-toolkit/scss/common.scss";
$prefixCls: user-elem-swj-datacard2-x64a8; // 定义变量
.#{$prefixCls} { // #{$xxx} 插值语法
display: block;
text-decoration: none;
position: relative;
&-item { // &- 继承上级,进行拼接 => user-elem-swj-datacard2-x64a8-item
width: 19%;
height: 128px;
&-info {
margin: 0 0 0 16px;
.count {
font-size: 24px;
color: #2D3E50;
font-weight: 700;
margin-right: 2px;
}
.unit {
font-size: 16px;
color: #2D3E50;
font-weight: 400;
}
}
}
}
// 混入mixin
@mixin theme($theme: DarkGray) {
background: $theme;
box-shadow: 0 0 1px rgba($theme, .25);
color: #fff;
}
.info {
@include theme;
}
.alert {
@include theme($theme: DarkRed);
}
.success {
@include theme($theme: DarkGreen);
}
16、关于换行 white-space
white-space 设置或检索对象内文本显示方式,通常我们使用于强制一行显示内容。 我们可以使用white-space: nowrap 强制文本文字内容不换行,在对象内一行显示完所有文字内容。
17、嗅探方法,识别客户端设备
// 表示当前是否在浏览器环境中运行(即非 Node.js 环境)
const inBrowser = typeof window !== 'undefined';
// 表示当前是否在 Weex 环境中运行(即非浏览器环境)。
const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;
// 表示当前的 Weex 平台类型(如 `android` 或 `ios`)。如果不在 Weex 环境中,则该变量值为undefined
const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();
// 表示当前浏览器的 user-agent 字符串,转换成小写字母后得到。
const UA = inBrowser && window.navigator.userAgent.toLowerCase();
// IE 浏览器
const isIE = UA && /msie|trident/.test(UA);
// IE9
const isIE9 = UA && UA.indexOf('msie 9.0') > 0;
// Edge 浏览器
const isEdge = UA && UA.indexOf('edge/') > 0;
// 安卓系统
const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');
// IOS系统
const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');
// 谷歌浏览器
const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge;
// 表示当前浏览器是否为 PhantomJS
const isPhantomJS = UA && /phantomjs/.test(UA);
// 表示当前浏览器的 Firefox 版本号。如果当前浏览器不是 Firefox,则该变量值为 null
const isFF = UA && UA.match(/firefox\/(\d+)/);
18、适配问题,响应式布局
/**
* 1、根据屏幕宽度动态设置根字体大小,rem布局
*/
const recalculate = () => {
// 获取html 元素
const docElement = document.documentElement || document.body
// 获取可视窗口宽度大小
let clientWidth = docElement.clientWidth
// 默认pc 端的设计稿宽度
let designWidth = 1440
if (clientWidth < 750) {
// 屏幕太小则使用移动端的设计稿宽度
designWidth = 640
} else if (clientWidth < designWidth) {
clientWidth -= 80
} else {
clientWidth = designWidth
}
docElement.style.fontSize = (clientWidth / designWidth) * 100 + 'px'
}
window.addEventListener('resize', recalculate)
/**
* 2、针对组件的适配问题,基于组件开发,但是项目主体未做适配不可控,采用媒体查询,选择性渲染两套样式,大屏适配
*/
// 可视屏幕的宽度最大为2300px,否则采用另一套样式
@media screen and (max-width: 2300px) {
body,
html {
font-size: 16px;
}
}
// 可视屏幕的宽度最低达到2300px,否则采用另一套样式
@media screen and (min-width: 2300px) {
body,
html {
font-size: calc(46px / 2);
}
}
19、获取客户端分辨率
document.documentElement.clientWidth // 可视窗口的宽度
window.screen.width // 客户端的屏幕的宽度
window.devicePixelRatio // 客户端的缩放比
const screenWidth = window.screen.width * window.devicePixelRatio
20、监听分辨率做适配
const [screenWidth, setScreenWidth] = useState<number>(window.screen.width * window.devicePixelRatio)
useEffect(() => {
const throttle = (fn: () => void, time: number) => {
let startDate = Date.now()
return (...test: any) => {
const endDate = Date.now()
if (endDate - startDate >= time) {
fn.apply(this, test)
startDate = Date.now()
}
}
}
const onResize = throttle(() => {
setScreenWidth(window.screen.width * window.devicePixelRatio)
}, 1000)
window.addEventListener('resize', onResize)
return () => {
window.removeEventListener('resize', onResize)
}
}, [screenWidth])
21、滚动条样式
/* WebKit */
1、可以设置滚动条的宽度和高度
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
2、可以设置滚动条的颜色和圆角
::-webkit-scrollbar-thumb {
background-color: #cccccc;
border-radius: 4px;
}
3、可以设置滚动条轨道的背景色
::-webkit-scrollbar-track {
background-color: #f0f0f0;
}
/* Firefox 火狐浏览器 */
scrollbar-color: #cccccc #f0f0f0;
scrollbar-width: thin;
22、对于url上的字符解析
encodeURI()
encodeURI() 函数通过将特定字符的每个实例替换为一个、两个、三或四转义序列来对统一资源标识符 (URI) 进行编码 (该字符的 UTF-8 编码仅为四转义序列) 由两个 "代理" 字符组成)。
encodeURI('123') // 123
encodeURI('钰见不止心动') // '%E9%92%B0%E8%A7%81%E4%B8%8D%E6%AD%A2%E5%BF%83%E5%8A%A8'
decodeURI()
decodeURI() 函数能解码由encodeURI 创建或其他流程得到的统一资源标识符(URI)。
decodeURI('123') // 123
decodeURI('%E9%92%B0%E8%A7%81%E4%B8%8D%E6%AD%A2%E5%BF%83%E5%8A%A8') // 钰见不止心动
23、关于组件通信
针对于兄弟组件之间的通信
- 一般采用状态提升,把两者之间的状态维护到共同的父组件中互相消费,父组件中被消费的状态改变了,其所有子组件也会重新被render(除非使用了缓存等优化手段)
import React, { useState } from 'react';
const Demo1 = (props: any) => {
const { data, onChange } = props;
console.log('###: 子组件Demo1 render')
return (
<div>
<div>{data}</div>
<button
onClick={() => {
onChange(Date.now());
}}
>date</button>
</div>
);
}
const Demo2 = (props: any) => {
const { data, onChange } = props;
console.log('###: 子组件Demo2 render')
return (
<div>
<div>{data}</div>
<button
onClick={() => {
onChange(Math.random());
}}
>随机数</button>
</div>
);
}
const App: React.FC<any> = () => {
const [data, setData] = useState<any>(666);
const handleChange = (value: any) => {
setData(value);
};
console.log('###: 父组件render')
return (
<div>
<Demo1 data={data} onChange={handleChange} />
<Demo2 data={data} onChange={handleChange} />
</div>
)
}
- 可以采用上下文去维护状态共同的状态,这样不管层级多深,组件之间的关系多复杂,都共同消费一个上下文(React.createContext() 配合useContext 使用),本质上其实也是将状态维护到上层供子项消费,就不必层层透传了
import React, { useState, useContext } from 'react';
const MyContext = React.createContext(666)
const Demo1 = (props: any) => {
console.log('###: 子组件Demo1 render')
return (
<div>
<div>demo1</div>
<Demo3 />
</div>
);
}
const Demo2 = (props: any) => {
const { data, onChange } = props
console.log('###: 子组件Demo2 render')
return (
<div>
<div>{data}</div>
<button
onClick={() => {
onChange(Math.random())
}}
>随机数</button>
</div>
);
}
const Demo3 = (props: any) => {
const value = useContext(MyContext);
console.log('###: 子组件Demo3 render')
return (
<div>
<div>demo3 -- {value}</div>
</div>
);
}
const App: React.FC<any> = () => {
const value = useContext(MyContext);
const [data, setData] = useState(value)
const handleChange = (value: any) => {
setData(value);
};
console.log('###: 父组件render')
return (
<div>
<MyContext.Provider value={data}>
<Demo1 />
</MyContext.Provider>
<Demo2 data={data} onChange={handleChange} />
</div>
)
}
- 利用ref 转发,配合useImperativeHandle 将目标组件的状态或者是方法暴露给需要消费的组件,这样可以避免不必要的渲染,更加精准的控制到组件的状态更新
import React, { useState, useRef, useImperativeHandle } from 'react';
const Demo1 = React.forwardRef((props: any, ref) => {
const [data, setData] = useState(666);
useImperativeHandle(ref, () => ({
demoData: data,
handleChange: (value: any) => {
setData(value);
}
}));
console.log('###: 子组件Demo1 render')
return (
<div>
<div>demo1 -- {data}</div>
</div>
);
})
const Demo2 = (props: any) => {
const { onChange } = props
console.log('###: 子组件Demo2 render')
return (
<div>
<div>demo2</div>
<button
onClick={() => {
onChange(Math.random())
}}
>随机数</button>
</div>
);
}
const App: React.FC<any> = () => {
const demoRef = useRef<any>(null);
console.log('###: 父组件render')
return (
<div>
<Demo1 ref={demoRef} />
<Demo2
onChange={(value: any) => {
const onChange = demoRef.current?.handleChange;
if (onChange) {
onChange(value);
}
}}
/>
</div>
)
}
- 当然也可以采用状态管理工具,没啥事就少用呗,毕竟很多替代方案,没有必要维护到全局的状态管理当中(当状态被组件消费时,状态改变,那么其消费的对应组件就会重新render,发布订阅模式)
24、关于伪元素配合var()、attr() 的妙用
1、使用var(),var可以读取css 的变量,同时可以被继承
.suffix {
position: relative;
}
.suffix::after {
content: var(--suffix, ''); // 取变量--suffix,默认为''
position: absolute;
top: 0;
}
<div style="--suffix: '后缀xxx'">
<div class='suffix'> </div>
</div>
2、使用attr(),attr可以读取元素的自定义属性,不可以继承
.suffix {
position: relative;
}
.suffix::after {
content: attr(data-suffix) '';; // 去元素的自定义属性data-suffix,默认为''
position: absolute;
top: 0;
}
<div class='suffix' data-suffix='后缀xxx'> </div>
25、一些鲜有人知但炸裂的css属性
all
它的主要作用是控制元素的所有样式属性是否应该被继承或重置。
需要注意的是,all 属性会应用于整个元素及其后代元素,因此可能会对页面性能产生影响。在实际使用时,建议谨慎使用该属性,仅在必要的情况下使用。
all: initial;:将元素的所有样式属性重置为初始值。all: inherit;:使元素继承其父元素的所有样式属性。all: unset;:类似于initial,但对于已经继承了父元素样式属性的元素,将恢复为继承状态。
<style>
div {
color: burlywood;
}
.btn {
color: red;
}
.ipt,
.btn {
all: unset;
}
</style>
<div>
<input type="text">
<input type="text" class="ipt">
<button class="btn">你好</button>
<button>你好</button>
</div>
resize
resize 属性用于指定元素是否可以被用户调整大小。它主要适用于 <textarea> 元素以及具有 overflow: auto 或 overflow: scroll 的块级元素。
<style>
div {
width: 100px;
height: 100px;
border: 1px solid red;
resize: auto;
overflow: auto;
}
</style>
<div></div>
inset
inset 属性用于设置元素的内边距、外边距和定位属性(top、right、bottom、left)的缩写
/* 应用于上、右、下和左 */
div {
inset: 10px;
}
/* 应用于上和下,应用于左和右 */
div {
inset: 10px 20px;
}
/* 应用于上,应用于左和右,应用于下 */
div {
inset: 10px 20px 30px;
}
/* 应用于上、右、下和左 */
div {
inset: 10px 20px 30px 40px;
}
<style>
.box {
width: 500px;
height: 500px;
border: 1px solid red;
margin: 0 auto;
position: relative;
}
.box>div {
width: 100px;
height: 100px;
border: 1px solid bisque;
line-height: 100px;
text-align: center;
position: absolute;
inset: 50%;
transform: translate(-50%, -50%)
}
</style>
<div class="box">
<div>居中</div>
</div>
27、关于React 数据透传的优化手段
import React, { useEffect, useMemo } from 'react';
const Demo = (props: any) => {
const { list } = props;
useEffect(() => {
// 当父组件重渲染时,副作用会不断执行,因为传入的list 引用被改变了
// 所以静态数据源尽量避免直接传入,可以提到组件外层,或者useMemo 缓存
}, [list]);
return (
<div></div>
);
}
// 1、提到组件外面,这样组件重渲染,源数据的引用也不会改变
const list = [
{ name: '你好', code: 1 },
{ name: '嗨咯', code: 2 }
];
const App = () => {
// 不建议,每次组件渲染的时候,数据都会重新创建,导致数据引用被改变
// const list = [
// { name: '你好', code: 1 },
// { name: '嗨咯', code: 2 }
// ];
// 2、采用useMemo 缓存数据,源数据的引用也不会改变
const list = useMemo(() => [
{ name: '你好', code: 1 },
{ name: '嗨咯', code: 2 }
], []);
return (
<Demo list={list} />
);
}
export default App
28、关于原生table 的合并单元格
table 的相关属性介绍
- <table>:表格元素
- <thead>:表头
- <tbody>:表体
- <tfoot>:表尾
- <th>:定义表格内的表头单元格
- <tr>:定义表格中的行
- <td>:定义表格中的单元格
- colspan:在HTML中,
colspan属性用于指定单元格跨越的列数,默认的colspan属性值是 1。这表示单元格默认只占据一个列的宽度,不会跨越其他列。如果未显式指定colspan属性,那么该单元格将默认具有colspan="1",也就是不跨越其他列。 - rowspan:在HTML中,
rowspan是一个用于<td>或<th>元素的属性,用于指定单元格跨越的行数。它的值是一个正整数,表示单元格应该跨越的行数。如果未显式指定rowspan属性,它的默认值为 1。这意味着单元格只占据当前行的一个单元格位置,不会跨越其他行。
原型图
效果图
注意点
- 表格的空间分配全是通过
colspan和rowspan来进行设置的,首先需要确定表格一共多少行多少列,直接数单元格数量去确定就OK了; - 如此原型图,横着数,最多有五列,但是每列的占比又不一样,那么就寻找他们的合适的比例(取整数),我这里是扩大了两倍,也就是看做了
18列,然后按照比例分配对应的colspan; - 竖着数,最多有
10行,虽然每行占比不一样,但是我们可以通过设置每一行td的高度实现; - 所以依据原型,那么我们就可以把其看成一个
10行 x 18列的表格,然后通过合并单元格,设置对应的高度就可以了;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
http-equiv="X-UA-Compatible"
content="IE=edge"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Document</title>
<style>
table {
/* 合并表格边框 */
border-collapse: collapse;
/* 设置边框间距为0 */
border-spacing: 0;
}
td {
border: 1px solid #333;
width: 20px;
height: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="box"></div>
<hr />
<script>
const data = [
[
{
colspan: 11,
rowspan: null,
width: 5.5,
height: 1.5,
text: '1',
},
{
colspan: 7,
rowspan: null,
width: 3.5,
height: 1.5,
text: '2',
},
],
[
{
colspan: 11,
rowspan: null,
width: 5.5,
height: 0.5,
text: '3',
},
{
colspan: 7,
rowspan: null,
width: 3.5,
height: 0.5,
text: '4',
},
],
[
{
colspan: 18,
rowspan: null,
width: 9,
height: 1.5,
text: '5',
},
],
[
{
colspan: 15,
rowspan: null,
width: 7.5,
height: 1.5,
text: '6',
},
{
colspan: 3,
rowspan: 6,
width: 1.5,
height: 6.7,
text: '7',
},
],
[
{
colspan: 15,
rowspan: null,
width: 7.5,
height: 0.8,
text: '8',
},
],
[
{
colspan: 15,
rowspan: null,
width: 7.5,
height: 1.9,
text: '9',
},
],
[
{
colspan: 15,
rowspan: null,
width: 7.5,
height: 1,
text: '10',
},
],
[
{
colspan: 9,
rowspan: 2,
width: 4.5,
height: 1.5,
text: '11',
},
{
colspan: 6,
rowspan: null,
width: 3,
height: 0.5,
text: '12',
},
],
[
{
colspan: 2,
rowspan: null,
width: 1,
height: 1,
text: '13',
},
{
colspan: 2,
rowspan: null,
width: 1,
height: 1,
text: '14',
},
{
colspan: 2,
rowspan: null,
width: 1,
height: 1,
text: '15',
},
],
[
{
colspan: 11,
rowspan: null,
width: 5.5,
height: 3.3,
text: '16',
},
{
colspan: 7,
rowspan: null,
width: 3.5,
height: 3.3,
text: '17',
},
],
];
const createTable = (data) => {
let table = '<table><tbody>';
for (let i = 0; i < data.length; i++) {
let tr = `<tr>`;
const trData = data[i];
for (let j = 0; j < trData.length; j++) {
const { colspan, rowspan, width, height, text } = trData[j];
const td = `<td colspan="${colspan}" rowspan="${
rowspan || 1
}" style="width: ${width}cm; height: ${height}cm">${text}</td>`;
tr += td;
}
tr += '</tr>';
table += tr;
}
return `${table}</tbody></table>`;
};
const table = createTable(data);
document.querySelector('.box').innerHTML = table;
console.log('###: ---', table);
console.log('###: ', 11);
</script>
<table>
<tbody>
<tr>
<td colspan="11" style="width: 5.5cm;">1</td>
<td colspan="7" style="width: 3.5cm;">2</td>
</tr>
<tr>
<td colspan="11" style="width: 5.5cm;">3</td>
<td colspan="7" style="width: 3.5cm;">4</td>
</tr>
<tr>
<td colspan="18" style="width: 9cm;">5</td>
</tr>
<tr>
<td colspan="15" style="width: 7.5cm;">6</td>
<td colspan="3" rowspan="6" style="width: 1.5cm;">7</td>
</tr>
<tr>
<td colspan="15" style="width: 7.5cm;">8</td>
</tr>
<tr>
<td colspan="15" style="width: 7.5cm;">9</td>
</tr>
<tr>
<td colspan="15" style="width: 7.5cm;">10</td>
</tr>
<tr>
<td colspan="9" rowspan="2" style="width: 4.5cm;">11</td>
<td colspan="6" style="width: 3cm;">12</td>
</tr>
<tr>
<td colspan="2" style="width: 1cm;">13</td>
<td colspan="2" style="width: 1cm;">14</td>
<td colspan="2" style="width: 1cm;">15</td>
</tr>
<tr>
<td colspan="11" style="width: 5.5cm;">16</td>
<td colspan="7" style="width: 3.5cm;">17</td>
</tr>
</tbody>
</table>
</body>
</html>