分类
javascript

层叠上下文与事件流

事件流

image - 层叠上下文与事件流

层叠上下文

若子元素的层叠上下文在父元素下方,那么点击子元素位置时target只能到父元素这,即子元素无法触发到点击监听

分类
常识

移动端手势

37960a877cae628edf0dbefbf164561b r 833x1024 - 移动端手势
分类
javascript react

React函数式编程使用lodash的防抖

import React, { FC, useRef } from 'react'
import copy from 'copy-to-clipboard'
import { message } from 'hljd'
import { debounce } from 'lodash'
interface ColorPickerInputNativeProps {
  value?: string
  onChange?: (value: string) => void
}
const ColorPickerInputNative: FC<ColorPickerInputNativeProps> = ({ value, onChange }) => {
  const eRef = useRef('')
  const funcRef = useRef(
    debounce(() => {
      copy(eRef.current)
      onChange?.(eRef.current)
      message.success('颜色值已复制')
    }, 200)
  )
  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    eRef.current = e.target.value
    funcRef.current()
  }
  return <input type="color" value={value} onChange={handleChange} />
}
export { ColorPickerInputNative }

要点:

  • 改变的值存入useRef中
  • 在useRef中放置debounce
分类
javascript

postMessage没接收到值只接收到webpackOk

原因

当时的iframe是动态生成的,未加载好就发送postMessage,webpack帮助了发送,所有出现webpackOk

解决方案

const targetIframe = document.getElementById('targetIframe')
const info = { sign: '标识' }
targetIframe.onload = () => {
    targetIframe.contentWindow.postMessage(info,'*')
}
分类
CSS

grid布局细节

grid-auto-flow和grid中的auto-flow

grid-auto-flow:流动方向,可以设定row或column,类似于flex-direction。

auto-flow:可以在grid中设置缩写

gird: <grid-template-rows> / [auto-flow && dense?] <grid-auto-columns>
grid: <auto-flow && dense?> <grid-auto-rows> / <grid-template-rows>

在左侧说明是row,在右侧说明是column,与grid-auto-flow有相同的作用。

例如

grid:auto-flow dense 300px/200px 1fr

上述代码说明有两列为200px和1fr,然后稠密的向下流动,只有匿名单元格,匿名单元格的高度为300px。

grid-column-start

计数以1开始,包括边缘线,就是说如果水平有6块,则计数可以从1~7,可以是负数-1是右侧边缘线,可以从-7~-1

grid-column-start 与 grid-column-end 不需要考虑前者的数字比后者大或小。

分类
react

hooks使用心得

在React 函数式编程中写定时器

let [count, setCount] = useState(0)
    let intervalCb = useRef(null)
    let CountTimer
    useEffect(() => {
        intervalCb.current = () => {
            setCount(count+1)   
        }
    }, [count])

    useEffect(() => {
        function itvFn() {
            intervalCb.current()
        }
        CountTimer = window.setInterval(itvFn, 1000)
        return () => window.clearInterval(CountTimer)
    }, [])

const handleStop = () => { // 暂停
        window.clearInterval(CountTimer)
    }

如何获取一次数据

import React, { useState, useEffect } from 'react';
import axios from 'axios';
 
function App() {
  const [data, setData] = useState({ hits: [] });
 
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        'https://hn.algolia.com/api/v1/search?query=redux',
      );
 
      setData(result.data);
    };
 
    fetchData();
  }, []);
 
  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
 
export default App;

useEffect尽量不要依赖不影响更新的依赖项

function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=redux',
  );
 
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(url);
 
      setData(result.data);
    };
 
    fetchData();
  }, [url]);
 
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>
 
      <ul>
        {data.hits.map(item => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </Fragment>
  );
}

将获取数据,loading,error的逻辑变为自定义钩子

import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';
 
const useDataApi = (initialUrl, initialData) => {
  const [data, setData] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
 
  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
 
      try {
        const result = await axios(url);
 
        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
 
      setIsLoading(false);
    };
 
    fetchData();
  }, [url]);
 
  return [{ data, isLoading, isError }, setUrl];
};
 
function App() {
  const [query, setQuery] = useState('redux');
  const [{ data, isLoading, isError }, doFetch] = useDataApi(
    'https://hn.algolia.com/api/v1/search?query=redux',
    { hits: [] },
  );
 
  return (
    <Fragment>
      <form
        onSubmit={event => {
          doFetch(
            `http://hn.algolia.com/api/v1/search?query=${query}`,
          );
 
          event.preventDefault();
        }}
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>
 
      {isError && <div>Something went wrong ...</div>}
 
      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}
 
export default App;
分类
typescript

typescript使用心得

keyof

类型判定时可能是interface中的某一个类型

interface ColorValue {
  hex: string
  hsl: ColorHSLValue
  rgb: ColorRGBAValue
}
interface ColorPickerInputProps {
  value?: ColorValue[keyof ColorValue] // 重点看这行
  mode?: 'hex' | 'hsl' | 'rgb'
  onChange?: (value: ColorValue[keyof ColorValue], event?: any) => void
  presetColors?: string[]
}
分类
react

mobx

定义

import { observable } from "mobx";

class Todo {
    id = Math.random();
    @observable title = "";
    @observable finished = false;
}
import { decorate, observable } from "mobx";

class Todo {
    id = Math.random();
    title = "";
    finished = false;
}
decorate(Todo, {
    title: observable,
    finished: observable
})

经过计算的属性

class TodoList {
    @observable todos = [];
    @computed get unfinishedTodoCount() {
        return this.todos.filter(todo => !todo.finished).length;
    }
}
分类
react

react-router-dom

最基本的路由

  • 在Route中加入exact确保精准匹配,用在主页面
  • 在Route下放置组件
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

export default function BasicExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>

        <hr />
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

// You can think of these components as "pages"
// in your app.

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}

如何使用路由参数

  • 首先引入Router包在外面(import { Router } from ‘react-router-dom’)
  • 引入跳转功能放在Router里面(import { Link } from ‘react-router-dom’)
  • 引入Switch加上切换页面的功能(import { Switch } from ‘react-router-dom’)
  • 引入Route,switch的子节点。(import { Route } from ‘react-router-dom’)
  • 在Route中定义路由参数和children
  • children的函数式组件可以使用useParams()获取路由参数对象
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useParams
} from "react-router-dom";

export default function ParamsExample() {
  return (
    <Router>
      <div>
        <h2>Accounts</h2>

        <ul>
          <li>
            <Link to="/netflix">Netflix</Link>
          </li>
          <li>
            <Link to="/zillow-group">Zillow Group</Link>
          </li>
          <li>
            <Link to="/yahoo">Yahoo</Link>
          </li>
          <li>
            <Link to="/modus-create">Modus Create</Link>
          </li>
        </ul>

        <Switch>
          <Route path="/:id" children={<Child />} />
        </Switch>
      </div>
    </Router>
  );
}

function Child() {
  // We can use the `useParams` hook here to access
  // the dynamic pieces of the URL.
  let { id } = useParams();

  return (
    <div>
      <h3>ID: {id}</h3>
    </div>
  );
}

嵌套的路由

  • 主要使用了useRouteMatch这个hook,这和匹配Route的原理是一样的
  • 从useRouteMatch从取得path和url,url是真实的跳转路径,path大多数时候和url一样,但是会有/path/:id这样的情况可用在Route里
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useParams,
  useRouteMatch
} from "react-router-dom";

export default function NestingExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>

        <hr />

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function Topics() {
  let { path, url } = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>
      <ul>
        <li>
          <Link to={`${url}/rendering`}>Rendering with React</Link>
        </li>
        <li>
          <Link to={`${url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${url}/props-v-state`}>Props v. State</Link>
        </li>
      </ul>

      <Switch>
        <Route exact path={path}>
          <h3>Please select a topic.</h3>
        </Route>
        <Route path={`${path}/:topicId`}>
          <Topic />
        </Route>
      </Switch>
    </div>
  );
}

function Topic() {
  let { topicId } = useParams();

  return (
    <div>
      <h3>{topicId}</h3>
    </div>
  );
}

有权限的路由

  • 在Link中我们有一个需要权限的路由跳转
  • 该需要权限的路由是获取useContext里的数据表示用户是否有权限
  • 有权限把children渲染进去,无权限重定向至/login

https://reactrouter.com/web/example/auth-workflow

匹配的路由给与修改

  • 不直接定义Link,给Link套一层
  • 在Link外层加入参数
  • 在函数内使用useRouterMatch(带参数)去匹配路由,如果匹配上添加一点样式

https://reactrouter.com/web/example/custom-link

当表单里有内容阻止页面离开

  • 需要一个数据来知道表单是否被修改过
  • 使用Prompt组件阻止离开

https://reactrouter.com/web/example/preventing-transitions

不匹配地址

  • 核心理念就是加入一个匹配任何页面的Route
  • 定义Route下的函数
  • 可以使用useLocation获取到想要到达的地址

https://reactrouter.com/web/example/no-match

分类
javascript

vue(js)转react(ts)

慢慢更新。。。。

定义

定义函数

// vue
methods: {
   fn(){},
}
// react
fn(){};
fb = ()=>{};

父子通信

子组件改父组件的值

vue

// 子组件
this.$emit('change',2);
// 父组件
<father @change="handleChange"></father>

handleChange(e) {
    console.log(e);
}

react

// 子组件
render() {}
this.props.handleChange(k)
// 父组件
fn(k){};
render() {
    return (<children handleChange={fn}></children>)
}

路由

react使用的是react-router-dom

class

如果要在同一个dom中写多个class

// vue
<div :class="['a',active?'b':'c']"></div>
// react
import cls from 'classnames';
return (
  <div className={cls(a,{b:active,c:!active})}></div>
)