[]

状态与副作用

useState

用于改变组件内的状态,例

const StateDemo: FC = () => {
    const [count, setCount] = useState(1);
    const [isShow, toggleShow] = useState(true);

    return (
        
{count}

{isShow ? I'm show now : null}

); }; export default StateDemo;

useEffect

在状态不同的生命周期执行副作用

简单用法

每次状态更新都执行所有没有依赖的useEffect,以下代码'toggle ghost'这一条在resize浏览器时也会触发

const EffectDemo: FC = () => {
    const [ghost, setGhost] = useState(false);
    const [width, setWidth] = useState(window.innerWidth);
    const toggleGhostBtn = () => setGhost(!ghost);
    const resizeHandle = () => setWidth(window.innerWidth);
    useEffect(() => {
        window.addEventListener('resize', resizeHandle);
    });
    useEffect(() => {
        console.log('toggle ghost');
    });
    return (
        
{ghost ? 'ghost' : '普通'}按钮

宽度为: {width}

); };

依赖更新

通过useEffect的第二个参数,可以指定其依赖的变量,只有此变量的状态更改时才会执行副作用函数,如果第二个参数为空,则只在第一次渲染和重新渲染时触发

const EffectDemo: FC = () => {
    ...
    useEffect(() => {
        // changeWidth
    }, [width]);
    useEffect(() => {
        console.log('toggle ghost');
    }, [ghost]);
   useEffect(() => {
        console.log('只在第一次或重新渲染组件时触发');
    }, []);
};

清理监听

在监听widthuseEffect中,每次改变width的状态,都会添加一个resize事件,这会极大的耗费浏览器占用的内存,通过一个返回值的方式,即可在下一次width状态改变后与添加新的resize监听前,取消上次添加的resize监听事件

const EffectDemo: FC = () => {
    ...
    useEffect(() => {
        window.addEventListener('resize', resizeHandle);
        return () => {
            window.removeEventListener('resize', resizeHandle);
        };
    }, [width]);
};

异步执行

useEffect中执行异步函数的语法如下,其实就是在原函数里调用一个async打头的立即函数

useEffect(() => {
    (async () => {})();
});

以下示例代码让按钮在变成ghost之后1s再变红色

const EffectDemo: FC = () => {
    const [red, setRed] = useState(false);
    useEffect(() => {
        (async () => {
            await new Promise((resolve, reject) => setTimeout(() => resolve(true), 1000));
            setRed(ghost);
        })();
    }, [ghost]);
    return (
        
); };

useLayoutEffect

useEffect几乎一样,但是具有防闪烁作用,比如下面的代码

const StateDemo: FC = () => {
    const [count, setCount] = useState(1);
    const [isShow, toggleShow] = useState(true);
    useLayoutEffect(() => {
        if (count === 0) setCount(Math.floor(Math.random() * 100));
    }, [count]);
    return (
        
{count} ...
); }; export default StateDemo;

如果使用useEffect在点击按钮时,它会在渲染屏幕后异步调用,而useLayoutEffect则会把所有逻辑先计算完毕最后一次性渲染,所以它会阻塞渲染,所以除非必须要用,一般情况下基本用useEffect

useContext

用于向后代组件透传一个值,以创建一个语言选择器为例

定义一个语言列表变量

const langs: LangType[] = [
    { name: 'en', label: 'english' },
    { name: 'zh-CN', label: '简体中文' },
];

创建一个context

const localContext = createContext({
    lang: langs[0],
    setLang: (lang: LangType) => {},
});

创建provider包装器

const LocalProvider: FC = ({ lang, setLang, children }) => {
    useEffect(() => {
        setLang(lang);
    }, [lang]);
    return (
        <>
            {children}
        
    );
};

创建Local组件

const Local: FC = ({ children }) => {
    const [lang, setLang] = useState(langs[0]);
    return (
        
            {children}
        
    );
};

App.tsx中的所有节点包含于Local组件

const App: FC = () => {
    return (
        
        ...
        
    );
};
export default App;

语言选择组件

const Lang: FC = () => {
    const { lang, setLang } = useContext(localContext);
    const changeLang = (value: string) => {
        const current = langs.find((item) => item.name === value);
        current && setLang(current);
    };
    return (
        <>
            
        
    );
};

显示当前语言

const CurrentLang: FC = () => {
    const { lang } = useContext(localContext);
    return 
当前语言: {lang.label || lang.name}
; };

App.tsx中使用以上两个组件

const App: FC = () => {
    return (
        
                ...
                
                
        
    );
};
export default App;

useReducer

使用Context+useReducer可以实现轻量级的全局状态管理

以实现一个简单的应用配置功能为例(包含标题设置和暗黑模式切换)

编写类型

// 可选择的主题模式
export type ThemeMode = 'light' | 'dark';
// 初始化应用配置
export type ConfigType = {
    title?: string;
    theme?: ThemeMode;
};
// 合并默认配置后的最终应用配置状态
export type ConfigStateType = Required;
// 可用的reducer操作
export enum ConfigureActionType {
    SET_TITLE = 'change_title',
    CHANGE_THEME = 'change_theme',
}
// 传入给dispatch触发器的数据
export type ConfigureAction =
    | { type: ConfigureActionType.SET_TITLE; value: string }
    | { type: ConfigureActionType.CHANGE_THEME; value: ThemeMode };
// 透传给子组件的context
export interface ConfigureContextType {
    state: ConfigStateType;
    dispatch: Dispatch;
}

创建Context

// 透传配置状态与dispatch
export const ConfigureContext = createContext(null);

状态操作

为了确保数据的唯一性不被污染,使用immer.js操作数据

export const configReducer: Reducer = produce((draft, action) => {
    switch (action.type) {
        // 设置标题
        case ConfigureActionType.SET_TITLE:
            draft.title = action.value;
            break;
        // 设置主题
        case ConfigureActionType.CHANGE_THEME:
            draft.theme = action.value;
            break;
        default:
            break;
    }
});

包装器组件

  • 合并默认配置和初始化配置
  • 使用useEffect创建在标题或主题状态改变时引发的副作用钩子
  • 把配置状态和dispatch传给ConfigureContext
const Configure: FC<{ config?: ConfigType }> = ({ config = {}, children }) => {
    const [state, dispatch] = useReducer(
        configReducer,
        config,
        (c) =>
            ({
                title: 'react app',
                theme: 'light',
                ...c,
            } as ConfigStateType),
    );
    useEffect(() => {
        const html = document.getElementsByTagName('html')[0];
        if (state.theme === 'dark') {
            html.classList.add('dark');
        } else {
            html.classList.remove('dark');
        }
    }, [state.theme]);
    useEffect(() => {
        document.title = state.title;
    }, [state.title]);
    return (
        
            {children}
        
    );
};

主题选择组件

const Theme = () => {
    const context = useContext(ConfigureContext);
    if (!context) return null;
    const { state, dispatch } = context;
    const toggleTheme = () =>
        dispatch({
            type: ConfigureActionType.CHANGE_THEME,
            value: state.theme === 'light' ? 'dark' : 'light',
        });
    return (
        
切换主题
); };

标题设置组件

const Title: FC = () => {
    const context = useContext(ConfigureContext);
    if (!context) return null;
    const { state, dispatch } = context;
    const changeTitle = (e: React.ChangeEvent) =>
        dispatch({
            type: ConfigureActionType.SET_TITLE,
            value: e.target.value,
        });
    return (
        
设置标题
); };

App.tsx中使用


  ...
  
  
</Configure>
</code></pre>
<h2>自定义Hooks</h2>
<p>为了更加便捷的使用<code>dispatch</code>,可以通过自定义一个hooks的方式来封装一些方法</p>
<pre><code class="language-typescript line-numbers">const useConfig = () => {
    const context = useContext(ConfigureContext);
    const { state = defaultConfig, dispatch } = context ?? {};
    const toggleTheme = () =>
        dispatch &&
        dispatch({
            type: ConfigureActionType.CHANGE_THEME,
            value: state.theme === 'light' ? 'dark' : 'light',
        });
    const changeTitle = (value: string) =>
        dispatch &&
        dispatch({
            type: ConfigureActionType.SET_TITLE,
            value,
        });
    return { config: state, toggleTheme, changeTitle };
};
</code></pre>
<p>有了自定的hooks之后就可以直接在组件中使用了</p>
<pre><code class="language-tsx line-numbers">const Theme: FC = () => {
    const {
        config: { theme },
        toggleTheme,
    } = useConfig();
    return (
        <div>
            <span>切换主题</span>
            <Switch
                checkedChildren="🌛"
                unCheckedChildren="☀️"
                onChange={toggleTheme}
                checked={theme === 'dark'}
                defaultChecked={theme === 'dark'}
            />
        </div>
    );
};
const Title: FC = () => {
    const {
        config: { title },
        changeTitle,
    } = useConfig();
    return (
        <div>
            <span>设置标题</span>
            <Input placeholder="标题" value={title} onChange={(e) => changeTitle(e.target.value)} />
        </div>
    );
};
</code></pre>
<h2>性能优化</h2>
<h3><code>useMemo</code></h3>
<p><code>useMemo</code>拥有个两个参数,一个回调函数和一个依赖项数组,回调函数必须返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值.</p>
<p>回调函数的返回值可以是一个<code>普通类型的值</code>(例如<code>字符串</code>,<code>布尔值</code>,<code>数组</code>,<code>对象</code>等)也可以是一个<code>函数</code>,甚至是一个<code>react组件</code>,如果返回值是一个函数,则其作用就与<code>useCallback</code>一样</p>
<p>以下代码在每次<code>config</code>发生改变时,另一个组件的<code>console.log</code>也会调用</p>
<pre><code class="language-tsx line-numbers">const Theme: FC = () => {
    const {
        config: { theme },
        toggleTheme,
    } = useConfig();
    console.log('render theme component');
    return (...组件代码);
};
const Title: FC = () => {
    const {
        config: { title },
        changeTitle,
    } = useConfig();
    console.log('render title component');
   return (...组件代码);
};
</code></pre>
<p>这样会在每次的<code>input</code>敲入一个字符时就会导致<code>Theme</code>组件重新渲染,极大的浪费了性能,可以通过<code>useMemo</code>做一下优化</p>
<pre><code class="language-tsx line-numbers">const Theme: FC = () => {
    const {
        config: { theme },
        toggleTheme,
    } = useConfig();
    return useMemo(() => {
        console.log('render theme component');
        return (...组件代码);
    }, [theme]);
};
const Title: FC = () => {
    const {
        config: { title },
        changeTitle,
    } = useConfig();
    return useMemo(() => {
        console.log('render title component');
        return (...组件代码);
    }, [title]);
};
</code></pre>
<p>现在更改其中一个组件只会执行自己组件里的<code>console.log</code>了</p>
<h3><code>useCallback</code></h3>
<p>现在把<code>Theme</code>和<code>Title</code>两个组件放在一起作为<code>ConfigPanel</code>的子组件,并取消原来的<code>useMemo</code>包装,而改用<code>memo</code>包装</p>
<blockquote><p>
  <code>React.memo</code>包装的组件,只有当<code>props</code>改变之后才会重新渲染,<code>memo</code>是浅对比
</p></blockquote>
<pre><code class="language-tsx line-numbers">const Theme: FC<{ theme: ThemeMode; toggleTheme: () => void }> = memo(({ theme, toggleTheme }) => {
    console.log('render theme component');
    return (...组件代码);
});
const Title: FC<{ title: string; changeTitle: (value: string) => void }> = memo(
    ({ title, changeTitle }) => {
        console.log('render title component');
        return (...组件代码);
    },
);
const ConfigPanel: FC = () => {
    const { config, toggleTheme, changeTitle } = useConfig();
    return (
        <>
            <Theme theme={config.theme} toggleTheme={toggleTheme} />
            <Title title={config.title} changeTitle={changeTitle} />
        </>
    );
};
</code></pre>
<p>这时会发现<code>Theme</code>子组件中执行<code>toggleTheme</code>也会导致<code>Title</code>组件重新渲染,原因是<code>changeTitle</code>函数不是固定的,父组件重选渲染后会导致产生新的<code>changeTitle</code>变量,现在尝试使用<code>useCallback</code>包装,是其只在<code>title</code>改变时才产生新值,<code>toggleTheme</code>也一样</p>
<pre><code class="language-typescript line-numbers">const useConfig = () => {
    const context = useContext(ConfigureContext);
    const { state = defaultConfig, dispatch } = context ?? {};
    const toggleTheme = useCallback(
        () =>
            dispatch &&
            dispatch({
                type: ConfigureActionType.CHANGE_THEME,
                value: state.theme === 'light' ? 'dark' : 'light',
            }),
        [state.theme],
    );

    const changeTitle = useCallback(
        (value: string) =>
            dispatch &&
            dispatch({
                type: ConfigureActionType.SET_TITLE,
                value,
            }),
        [state.title],
    );
    return { config: state, toggleTheme, changeTitle };
};
</code></pre>
<p>现在执行<code>toggleTheme</code>并不会导致<code>Title</code>组件重新渲染了,反之亦然</p>
<h2>组件引用</h2>
<h3><code>useRef</code></h3>
<p>创建<code>ref</code>对象,其<code>.current</code>属性被初始化为传入的参数,其<code>current</code>属性是可以通过赋值主动改变,而 <code>ref</code> 对象本身在组件的整个生命周期内保持不变</p>
<h4>生命周期不变对象</h4>
<p>以下代码通过使用<code>useRef</code>保存上一次的变量,无论<code>count</code>如何改变都不会执行<code>console.log</code>,因为<code>ref</code>对象本身是不变的.而由于<code>useEffect</code>和<code>useLayoutEffect</code>都是生命周期钩子,与外部是异步的,所以<code>ref.current</code>虽然会在钩子中被赋值为最新值,而其外部则保持上一次的值.</p>
<pre><code class="language-tsx line-numbers">const StateDemo: FC = () => {
    ...
    const ref = useRef(count);
    useLayoutEffect(() => {
        ref.current = count;
        if (count === 0) setCount(Math.floor(Math.random() * 100));
    }, [count]);
    useEffect(() => {
        console.log('ref has changed');
    }, [ref]);
    return (
        <div>
            {count}
            {ref.current}
            <Button onClick={() => setCount(count + 1)}>增加</Button>
            ...
        </div>
    );
};

</code></pre>
<h4>与<code>forwardRef</code>结合</h4>
<p>通过<code>forwardRef</code>可以把<code>useRef</code>的值与<code>dom</code>节点绑定,从而可以操控原生的<code>dom</code>节点</p>
<pre><code class="language-tsx line-numbers">const CustomInput = forwardRef((props = {}, ref: Ref<any>) => (
    <input ref={ref} type="text" {...props} />
));
const RefDemo: FC = () => {
    const inputRef = useRef<HTMLInputElement | null>(null);

    useEffect(() => {
        if (inputRef.current) {
            inputRef.current.focus();
            inputRef.current.value = 'useRef';
        }
    });
    return (
        <>
            <CustomInput ref={inputRef} />
        </>
    );
};
export default RefDemo;
</code></pre>
<h3><code>useImperativeHandle</code></h3>
</div>                </div><!-- .entry-content -->

                
<footer class="entry-footer">
    <div itemprop="author" itemscope itemtype="https://schema.org/Person">
        <meta itemprop="name" content="pincman" />
        <meta itemprop="url" content="https://v.pincman.com/author/pincman" />
    </div>

    <meta itemprop="datePublished" content="2021-10-29T14:27:28+08:00" />
    <!-- <time itemprop="dateModified" datetime="2023-04-05T03:57:26+08:00">
        最后修改于 2023年4月5日    </time> -->
</footer>
<div id="comments" class="entry-comments">
    	<div id="respond" class="comment-respond">
		<h3 id="reply-title" class="comment-reply-title">发表评论 <small><a rel="nofollow" id="cancel-comment-reply-link" href="/docs/react-ts/hooks#respond" style="display:none;">取消回复</a></small></h3><form action="https://v.pincman.com/wp-comments-post.php" method="post" id="commentform" class="comment-form" novalidate><p class="comment-notes"><span id="email-notes">您的电子邮箱地址不会被公开。</span> <span class="required-field-message" aria-hidden="true">必填项已用<span class="required" aria-hidden="true">*</span>标注</span></p><div class="comment-form-comment"><textarea id="comment" name="comment" class="required" rows="4" placeholder="请输入评论内容..."></textarea></div><div class="comment-form-author"><input id="author" name="author" type="text" placeholder="*昵称: " value="" size="30" class="required"></div>
<div class="comment-form-email"><input id="email" name="email" type="text" placeholder="*邮箱: " value="" class="required"></div>
<div class="comment-form-url"><input id="url" name="url" type="text" placeholder="网址: " value="" size="30"></div>
<div class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" checked="checked" > 浏览器会保存昵称、邮箱和网站cookies信息,下次评论时使用。</div>
<div class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="提交" /> <input type='hidden' name='comment_post_ID' value='61' id='comment_post_ID' />
<input type='hidden' name='comment_parent' id='comment_parent' value='0' />
</div></form>	</div><!-- #respond -->
		</div>
            </div><!-- .docspress-single-content -->
        </article><!-- #post-## -->
    </div>
    </main>
</div>
</main><!-- #main -->

<footer class="site-footer">
		<div class="footer-copyright d-flex text-center">
		<div class="container">
			<p class="m-0 small">
				Copyright © 2022 <a href="https://pincman.com/docs/rowfish">rowfish</a> - All rights reserved<span class="sep"> | </span><a href="https://beian.miit.gov.cn" target="_blank" rel="noreferrer nofollow">浙ICP备18013418号 - 6</a>											</p>
		</div>
	</div>

</footer><!-- #footer -->

</div><!-- #page -->

<div class="rollbar">
		<ul class="actions">
			<li>
						<a href="http://wpa.qq.com/msgrd?v=3&uin=1849600177&site=qq&menu=yes" rel="nofollow noopener noreferrer"><i class="fas fa-headset"></i><span>站长</span></a>
		</li>
				<li>
						<a href="https://v.pincman.com/user?action=vip" rel="nofollow noopener noreferrer"><i class="fab fa-codepen"></i><span>订阅</span></a>
		</li>
				<li>
						<a href="https://v.pincman.com/user" rel="nofollow noopener noreferrer"><i class="far fa-user"></i><span>我的</span></a>
		</li>
			</ul>
		<div id="back-to-top" class="rollbar-item" title="返回顶部">
		<i class="fas fa-chevron-up"></i>
	</div>
</div>

<div class="dimmer"></div>

<div class="off-canvas">
  <div class="canvas-close"><i class="fas fa-times"></i></div>
  <div class="mobile-menu d-block d-xl-none d-lg-none"></div>
</div><div id="omnisearch" class="omnisearch">
    <div class="container">
        <form class="omnisearch-form" method="get" action="https://v.pincman.com/">
            <div class="form-group">
                <div class="input-group input-group-merge input-group-flush">
                    <div class="input-group-prepend">
                        <span class="input-group-text"><i class="fas fa-search"></i></span>
                    </div>
                    <input type="text" class="search-ajax-input form-control" name="s" value="" placeholder="输入关键词 回车搜索..." autocomplete="off">

                </div>
            </div>
        </form>
        <div class="omnisearch-suggestions">
            <div class="search-keywords">
                <span class='badge badge-secondary' data-toggle='tooltip' data-placement='right' data-delay='0' title='文章分类'><i class='far fa-folder-open'></i></span>
<span class='badge badge-danger' data-toggle='tooltip' data-placement='right' data-delay='0' title='文章标签'><i class='fas fa-tag'></i></span>
<a href="https://v.pincman.com/course_categories/golang" class="tag-cloud-link tag-link-8 tag-link-position-1" style="font-size: 14px;">Go语言</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
<a href="https://v.pincman.com/course_categories/nodejs" class="tag-cloud-link tag-link-6 tag-link-position-2" style="font-size: 14px;">Node.js</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
<a href="https://v.pincman.com/course_categories/php" class="tag-cloud-link tag-link-9 tag-link-position-3" style="font-size: 14px;">PHP</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
<a href="https://v.pincman.com/course_categories/frontend" class="tag-cloud-link tag-link-7 tag-link-position-4" style="font-size: 14px;">前端</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
<a href="https://v.pincman.com/course_categories/tools" class="tag-cloud-link tag-link-11 tag-link-position-5" style="font-size: 14px;">工具</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
<a href="https://v.pincman.com/course_categories/mobile" class="tag-cloud-link tag-link-55 tag-link-position-6" style="font-size: 14px;">移动</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
<a href="https://v.pincman.com/course_categories/server" class="tag-cloud-link tag-link-10 tag-link-position-7" style="font-size: 14px;">运维</a><span class='badge badge-success' data-toggle='tooltip' data-placement='right' data-delay='0' title='课程分类'><i class='fas fa-video'></i></span>
            </div>
                    </div>
    </div>
</div>
<script src='https://v.pincman.com/wp-includes/js/jquery/jquery.form.min.js' id='jquery-form-js'></script>
<script src='https://v.pincman.com/wp-includes/js/underscore.min.js' id='underscore-js'></script>
<script src='https://v.pincman.com/wp-includes/js/backbone.min.js' id='backbone-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer/assets/js/lib/selectize.min.js' id='selectize-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer/assets/js/common.js' id='anspress-common-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer/assets/js/list.js' id='anspress-list-js'></script>
<script id='anspress-tags-js-extra'>
var apTagsTranslation = {"deleteTag":"\u5220\u9664\u6807\u7b7e","addTag":"\u6dfb\u52a0\u6807\u7b7e","tagAdded":"\u5df2\u6dfb\u52a0\u81f3\u6807\u7b7e\u5217\u8868","tagRemoved":"\u4ece\u6807\u7b7e\u5217\u8868\u4e2d\u5220\u9664\u3002","suggestionsAvailable":"\u63d0\u4f9b\u5efa\u8bae\u3002 \u4f7f\u7528\u4e0a\u4e0b\u7bad\u5934\u952e\u8fdb\u884c\u9605\u8bfb\u3002"};
</script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer/assets/js/tags.js' id='anspress-tags-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/docspress/assets/vendor/anchor-js/anchor.min.js' id='anchor-js-js'></script>
<script id='docspress-js-extra'>
var docspress_vars = {"ajaxurl":"https:\/\/v.pincman.com\/wp-admin\/admin-ajax.php","nonce":"71f841d464"};
</script>
<script src='https://v.pincman.com/wp-content/plugins/docspress/assets/js/script.min.js' id='docspress-js'></script>
<script id='toc-front-js-extra'>
var tocplus = {"smooth_scroll":"1"};
</script>
<script src='https://v.pincman.com/wp-content/plugins/table-of-contents-plus/front.min.js' id='toc-front-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/KaTeX/katex.min.js' id='Katex-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Mermaid/mermaid.min.js' id='Mermaid-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/MindMap/mindMap.min.js' id='MindMap-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Emojify.js/js/emojify.min.js' id='Emojify.js-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/ClipBoard/clipboard.min.js' id='copy-clipboard-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/components/prism-core.min.js' id='prism-core-js-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/plugins/autoloader/prism-autoloader.min.js' id='prism-plugin-autoloader-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/plugins/toolbar/prism-toolbar.min.js' id='prism-plugin-toolbar-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/plugins/line-numbers/prism-line-numbers.min.js' id='prism-plugin-line-numbers-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/plugins/show-language/prism-show-language.min.js' id='prism-plugin-show-language-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js' id='prism-plugin-copy-to-clipboard-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Editormd/editormd.min.js' id='Editormd_Front-js'></script>
<script id='Config_Front-js-extra'>
var _Editormd = {"editormdUrl":"https:\/\/v.pincman.com\/wp-content\/plugins\/wp-editormd","syncScrolling":"on","livePreview":"on","htmlDecode":"on","imageLink":"on","toc":"on","theme":"dark","previewTheme":"dark","editorTheme":"gruvbox-dark","emoji":"on","tex":"katex","taskList":"on","imagePaste":"on","prismTheme":"dark","prismLineNumbers":"on","mindMap":"on","mindMapURL":"https:\/\/v.pincman.com\/wp-content\/plugins\/wp-editormd\/assets\/MindMap\/mindMap.min.js","mermaid":"on","placeholderEditor":"\u4eab\u53d7Markdown\u5199\u4f5c\u5feb\u611f\u5427\uff01","imgUploading":"\u56fe\u7247\u4e0a\u4f20\u4e2d...","imgUploadeFailed":"\u56fe\u7247\u4e0a\u4f20\u5931\u8d25\uff01","supportComment":"on","supportOther":""};
</script>
<script src='https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Config/editormd.min.js' id='Config_Front-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer//addons/syntaxhighlighter/syntaxhighlighter/scripts/shCore.js' id='syntaxhighlighter-core-js'></script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer//addons/syntaxhighlighter/syntaxhighlighter/scripts/shAutoloader.js' id='syntaxhighlighter-autoloader-js'></script>
<script id='syntaxhighlighter-js-before'>
			aplang = aplang||{};
			aplang.shLanguage = '语言';
			aplang.shInline = '是内联的吗?';
			aplang.shTxtPlholder = '在此处插入代码段...';
			aplang.shButton = '插入编辑器';
			aplang.shTitle = '插入代码';

			window.apBrushPath = "https://v.pincman.com/wp-content/plugins/anspress-question-answer//addons/syntaxhighlighter/syntaxhighlighter/scripts/";
		
</script>
<script src='https://v.pincman.com/wp-content/plugins/anspress-question-answer/addons/syntaxhighlighter/script.js' id='syntaxhighlighter-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/js/popper.min.js' id='popper-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/bootstrap/js/bootstrap.min.js' id='bootstrap-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/js/html2canvas.min.js' id='html2canvas-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/jarallax/jarallax.min.js' id='jarallax-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/jarallax/jarallax-video.min.js' id='jarallax-video-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/js/plugins.js' id='plugins-js'></script>
<script id='app-js-extra'>
var rizhutiv2 = {"home_url":"https:\/\/v.pincman.com","admin_url":"https:\/\/v.pincman.com\/wp-admin\/admin-ajax.php","comment_list_order":"asc","infinite_load":"\u52a0\u8f7d\u66f4\u591a","infinite_loading":"\u52a0\u8f7d\u4e2d...","jquey":"1","pay_type_html":{"html":"<div class=\"pay-button-box\"><div class=\"pay-item\" id=\"weixinpay\" data-type=\"12\"><i class=\"weixinpay\"><\/i><span>\u5fae\u4fe1\u652f\u4ed8<\/span><\/div><\/div>","alipay":0,"weixinpay":12,"iconpay":99,"cdkpay":0},"singular_id":"61"};
</script>
<script src='https://v.pincman.com/wp-content/themes/rizhuti-v2/assets/js/app.js' id='app-js'></script>
<script src='https://v.pincman.com/wp-includes/js/comment-reply.min.js' id='comment-reply-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rowfish/assets/js/plugins/popper.min.js' id='rowfish_popper-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rowfish/assets/js/plugins/anspress.js' id='rowfish_anspress-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rowfish/assets/js/plugins/tippy-bundle.umd.min.js' id='tippy-js'></script>
<script src='https://v.pincman.com/wp-content/themes/rowfish/assets/js/app.js' id='rowfish_app-js'></script>
        <script type="text/javascript">
            (function ($) {
                $(document).ready(function () {
                    $(".katex.math.inline").each(function () {
                        var parent = $(this).parent()[0];
                        if (parent.localName !== "code") {
                            var texTxt = $(this).text();
                            var el = $(this).get(0);
                            try {
                                katex.render(texTxt, el);
                            } catch (err) {
                                $(this).html("<span class=\"err\">" + err);
                            }
                        } else {
                            $(this).parent().text($(this).parent().text());
                        }
                    });
                    $(".katex.math.multi-line").each(function () {
                        var texTxt = $(this).text();
                        var el = $(this).get(0);
                        try {
                            katex.render(texTxt, el, {displayMode: true})
                        } catch (err) {
                            $(this).html("<span class=\"err\">" + err)
                        }
                    });
                })
            })(jQuery);
        </script>
                <script type="text/javascript">
            (function ($) {
                $(document).ready(function () {
                    $(".mermaid script").remove();
                    mermaid.initialize({
    "theme": "dark",
    "logLevel": 5,
    "arrowMarkerAbsolute": false,
    "startOnLoad": true,
    "flowchart": {
        "htmlLabels": true,
        "curve": "linear"
    },
    "sequence": {
        "diagramMarginX": 50,
        "diagramMarginY": 10,
        "actorMargin": 50,
        "width": 150,
        "height": 65,
        "boxMargin": 10,
        "boxTextMargin": 5,
        "noteMargin": 10,
        "messageMargin": 35,
        "mirrorActors": true,
        "bottomMarginAdj": 1,
        "useMaxWidth": true
    },
    "gantt": {
        "titleTopMargin": 25,
        "barHeight": 20,
        "barGap": 4,
        "topPadding": 50,
        "leftPadding": 75,
        "gridLineStartPadding": 35,
        "fontSize": 11,
        "fontFamily": "\"Open-Sans\", \"sans-serif\"",
        "numberSectionStyles": 4,
        "axisFormat": "%Y-%m-%d"
    },
    "class": {},
    "git": {}
},".mermaid");
                })
            })(jQuery)
        </script>
                <script type="text/javascript">
            (function ($) {
                $(document).ready(function () {
                    $(".mind p").remove();
                    $(".mind .mindTxt script").remove();
                    var mind = $(".mind");
                    if (mind.drawMind !== undefined) {
                        mind.drawMind();
                    }
                })
            })(jQuery)
        </script>
                <script type="text/javascript">
            window.onload = function () {
                emojify.setConfig({
                    img_dir: "https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Emojify.js/images/basic",//前端emoji资源地址
                    blacklist: {
                        "ids": [],
                        "classes": ["no-emojify"],
                        "elements": ["^script$", "^textarea$", "^pre$", "^code$"]
                    }
                });
                emojify.run();
            }
        </script>
                <script type="text/javascript">
            Prism.plugins.autoloader.languages_path = "https://v.pincman.com/wp-content/plugins/wp-editormd/assets/Prism.js/components/";
        </script>
        
<!-- 自定义js代码 统计代码 -->
<!-- 自定义js代码 统计代码 END -->

</body>

</html>