金点网络-全网资源,一网打尽
  • 网站首页
    • 金点部落
    • 小游戏
    • OpenAPI
    • 设计资产导航
    • 升级会员
  • 技能学习
    • 体育运动
    • 办公教程
    • 口才演讲
    • 小吃技术
    • 建站教程
    • 摄影教程
    • 棋牌教程
    • 网赚教程
      • 爆粉引流
      • 自媒体
      • 贴吧引流
  • 网站源码
    • 商城/淘客/交易
    • 小说/漫画/阅读
    • 影视/音乐/视频
    • 微信/微商/微擎
    • 理财/金融/货币
    • 模板/主题/插件
  • 游戏源码
    • 精品网单
    • 端游源码
    • 手游源码
    • 页游源码
  • 素材资料
    • 电子文档
    • 综合资料
    • 考研资料
    • 设计素材
    • 音频讲座
      • 人文艺术
      • 名师讲座
      • 说书小说
  • 软件工具
    • Windows软件
    • MacOS软件
    • Android软件
  • 寄售资源
    • 游戏源码
    • 网站源码
    • 软件源码
  • 公益服
登录/注册
  • 专享大神特权
立即开通开通会员抄底价

Note·React Hook

作者 : jamin 本文共9872个字,预计阅读时间需要25分钟 发布时间: 2022-11-22 共1549人阅读

React Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

拥抱 React Hook

什么是 Hook?

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用。

什么时候使用 Hook?

如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class,而现在你可以在现有的函数组件中使用 Hook。

State Hook

State Hook 是允许你在 React 函数组件中添加 state 的 Hook。在 class 中,可以通过在构造函数中设置 this.state 来初始化 state,但是在函数组件中,我们没有 this,所以不能分配或读取 this.state,我们直接在组件中调用 useState,举个栗子:

import React, { useState } from 'react'

export default function Hello(prop) {
  const [name, setName] = useState('nicestar')
  const handleChange = e => setName(e.target.value)

  return (
    <div>
      <Input placeholder="Your name" value={name} onChange={handleChange} />
    </div>
  )
}

useState 是 react 提供的新方法,这是一种在函数调用时保存变量的方式,它与 class 里面的 this.state 提供的功能完全相同。一般来说,在函数退出后变量就就会”消失”,而 state 中的变量会被 React 保留。

useState 方法里面唯一的参数就是初始 state。不同于 class 初始 state 必须是对象类型,useState 的参数可以是数字或者字符串等类型而不一定是对象。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用。

useState 调用后会返回当前 state 以及更新 state 的函数,可以通过数组的解构赋值来获取。不像 class 中的 this.setState,更新 state 变量总是替换它而不是合并它。

当然,如果存在多个表单域,最好的实现方式是将 Hook 提取出复用的函数:

import React, { useState } from 'react'

export default function Hello(prop) {
  const name = useFormInput('nicestar')
  const age = useFormInput('24')

  return (
    <div>
      <Input placeholder="Your name" value={name.value} onChange={name.onChange} />
      <Input placeholder="Your age" value={age.value} onChange={age.onChange} />
    </div>
  )
}

function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue)
  const handleChange = e => setValue(e.target.value)

  return {
    value,
    onChange: handleChange
  }
}

如果计算初始值代价昂贵,可以传入函数,这样只会执行一次:

function Table(props) {
  // ⚠️ createRows() 每次渲染都会被调用
  const [rows, setRows] = useState(createRows(props.count))

  // ✅ createRows() 只会被调用一次
  const [rows, setRows] = useState(() => createRows(props.count))
}

Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作。数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。React 组件中常见副作用一般分不需要清除和需要清除两种类型。

不需要清除的 Effect

这里先举个不需要清除副作用的栗子,我们根据表单输入内容来动态改变页面标签标题:

import React, { useState, useEffect } from 'react'

export default function Hello(prop) {
  const name = useFormInput('nicestar')

  const title = `Hello, ${name.value}`
  useDocumentTitle(title)

  return (
    <div>
      <Input placeholder="Your name" value={name.value} onChange={name.onChange} />
    </div>
  )
}

function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue)
  const handleChange = e => setValue(e.target.value)

  return {
    value,
    onChange: handleChange
  }
}

function useDocumentTitle(title) {
  useEffect(() => {
    document.title = title
  })
}

useEffect 可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在上面例子的 effect 中,传递的函数设置了 document 的 title 属性,每次 DOM 更新后都会调用该函数。

将 useEffect 放在组件内部让我们可以在 effect 中直接访问 state 变量或其他 props。Hook 使用了 JavaScript 的闭包机制,将它保存在函数作用域中。。

默认情况,useEffect 会在每次渲染后执行。当然也可以通过跳过 Effect 进行性能优化,这部分接下来细说。

传递给 useEffect 的函数在每次渲染中都会有所不同,这是刻意为之的。每次重新渲染,都会生成新的 effect,替换掉之前的。某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。

如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。 与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。

需要清除的 Effect

上面的动态修改标签页标题的副作用属于不需要清除的副作用,而事件监听器属于需要清除的副作用。为了防止引起内存泄露,在 class 组件中,会在 componentDidMount 添加的事件监听,然后在 componentWillUnmount 生命周期中移除事件监听,但这样会让处理同一个功能逻辑的代码分布在两个不同的地方,即使这两部分代码都作用于相同的副作用。

而在函数组件中 useEffect 的处理方式就高明许多,useEffect 设计是让属于同一副作用的代码在同一个地方执行。如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它。这里再举个栗子说明,现在我们要让组件加载时设置监听窗口缩放的事件,组件销毁时移除:

import React, { useState, useEffect } from 'react'

export default function Hello(prop) {
  const width = useWindowWidth()

  return (
    <div>
      <div>Width: {width}</div>
    </div>
  )
}

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)
  const handleWindowResize = () => setWidth(window.innerWidth)

  useEffect(() => {
    window.addEventListener('resize', handleWindowResize, false)
    // 这里返回一个函数,React 将会在执行清除操作时调用它
    return () => window.removeEventListener('resize', handleWindowResize)
  })

  return width
}

为什么要在 effect 中返回一个函数? 这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起,它们都属于 effect 的一部分。

Effect 关注点

使用 Effect Hook 其中一个目的就是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。

Hook 允许我们按照代码的用途分离他们,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的每一个 effect。它会在调用一个新的 effect 之前对前一个 effect 进行清理。

在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 componentDidUpdate 中添加对 prevProps 或 prevState 的比较逻辑解决。

componentDidUpdate(prevProps, prevState) {
  if (prevState.name !== this.state.name) {
    document.title = `Hello, ${this.state.name}`
  }
}

在 Effect Hook 中,判断是否需要重新执行的逻辑更为简单,它被内置到了 useEffect 的 Hook API 中。只要传递数组作为 useEffect 的第二个可选参数,React 会判断数组中的值在两次渲染之间有没有发生变化,来决定是否跳过对 effect 的调用,从而实现性能优化。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

useEffect(() => {
  document.title = `Hello, ${this.state.name}`
}, [name])

需要注意:如果要使用此优化方式,请确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量。

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。

如果你传入了一个空数组([]),effect 内部的 props 和 state 就会一直拥有其初始值。

React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect。

还有一点是 effect 的依赖频繁变化时,在 effect 内使用 setValue,可以传入函数而不是传入值:

function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1) // 这个 effect 依赖于 `count` state
    }, 1000)
    return () => clearInterval(id)
  }, []) // 🔴 Bug: `count` 没有被指定为依赖

  return <h1>{count}</h1>
}

function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1) // ✅ 在这不依赖于外部的 `count` 变量
    }, 1000)
    return () => clearInterval(id)
  }, []) // ✅ 我们的 effect 不适用组件作用域中的任何变量

  return <h1>{count}</h1>
}

Context Hook

useContext 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。useContext 的参数必须是 context 对象本身。

useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 <MyContext.Consumer>。

当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。调用了 useContext 的组件总会在 context 值变化时重新渲染。

import React, { useContext } from 'react'
import GlobalContext from '../../context'

export default function Hello(prop) {
  const local = useContext(GlobalContext)

  return (
    <div>
      <div>Language: {local}</div>
    </div>
  )
}

Reducer Hook

在之前的 State Hook 介绍中,我们将多个表单的 useState 提取出单独的函数来处理:

function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue)
  const handleChange = e => setValue(e.target.value)

  return {
    value,
    onChange: handleChange
  }
}

这是 useReducer 的雏形,React 内置了 useReducer 用来管理状态。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

当 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 时候,可以使用 useReducer 代替 useState。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化。

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState)

  function dispatch(action) {
    const nextState = reducer(state, action)
    setState(nextState)
  }

  return [state, dispatch]
}

调用方式:

function todosReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    // ... other actions ...
    default:
      return state
  }
}

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer, [])

  function handleAddClick(text) {
    dispatch({ type: 'add', text })
  }
  // ...
}

Callback Hook

useCallback 把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。

依赖项数组不会作为参数传给回调函数。虽然从概念上来说它表现为:所有回调函数中引用的值都应该出现在依赖项数组中。

使用 callback ref 可以获取 DOM:

function MeasureExample() {
  const [height, setHeight] = useState(0)

  const measuredRef = useCallback(node => {
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height)
    }
  }, []) // [] 作为 useCallback 的依赖列表,这确保了 ref callback 不会在再次渲染时改变

  return (
    <>
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(height)}px tall</h2>
    </>
  )
}

或者可以单独提取出可复用得 Hook:

function MeasureExample() {
  const [rect, ref] = useClientRect()
  return (
    <>
      <h1 ref={ref}>Hello, world</h1>
      {/* 这里使用短路运算 */}
      {rect !== null && <h2>The above header is {Math.round(rect.height)}px tall</h2>}
    </>
  )
}

function useClientRect() {
  const [rect, setRect] = useState(null)
  const ref = useCallback(node => {
    if (node !== null) {
      setRect(node.getBoundingClientRect())
    }
  }, [])
  return [rect, ref]
}

Memo Hook

useMemo 返回一个 memoized 值,把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])

Ref Hook

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

const refContainer = useRef(initialValue)

查看官方示例:

function TextInputWithFocusButton() {
  const inputEl = useRef(null)
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus()
  }

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  )
}

useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象。

Ref Hook 不仅可以用于 DOM refs。「ref」对象是一个 current 属性可变且可以容纳任意值的通用容器,类似于一个 class 的实例属性。

function Timer() {
  const intervalRef = useRef()

  useEffect(() => {
    const id = setInterval(() => {
      console.log('tick')
    })
    // 通过 .current 属性来记录定时器 id
    intervalRef.current = id

    // 回调在组件销毁时清除
    return () => {
      clearInterval(intervalRef.current)
    }
  })

  // 或者可以手动清除
  function handleCancelClick() {
    clearInterval(intervalRef.current)
  }
}

甚至可以用它来保存上一轮得 props 或 state:

function Counter() {
  const [count, setCount] = useState(0)
  const prevCount = usePrevious(count)
  return (
    <h1>
      Now: {count}, before: {prevCount}
    </h1>
  )
}

function usePrevious(value) {
  const ref = useRef()
  useEffect(() => {
    ref.current = value
  })
  return ref.current
}

从概念上讲,可以认为 refs 就像是一个 class 的实例变量。除非你正在做懒加载,否则避免在渲染期间设置 refs —— 这可能会导致意外的行为。相反的,通常你应该在事件处理器和 effects 中修改 refs。

ImperativeHandle Hook

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。useImperativeHandle 应当与 forwardRef 一起使用:

function FancyInput(props, ref) {
  const inputRef = useRef()
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    }
  }))
  return <input ref={inputRef} />
}
FancyInput = forwardRef(FancyInput)

LayoutEffect Hook

useLayoutEffect 与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。尽可能使用标准的 useEffect 以避免阻塞视觉更新。

DebugValue Hook

useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。

// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline')

Hook 规则

Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则:

  1. 只在最顶层使用 Hook。不要在循环、条件或嵌套函数中调用 Hook,确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。
  2. 只在 React 函数中调用 Hook。不要在普通的 JavaScript 函数中调用 Hook。

React 依靠的是 Hook 调用的顺序来确定哪个 state 对应哪个 useState,所以一定要确保每次渲染时候的 Hook 顺序是一致的。只有 Hook 的调用顺序在每次渲染中都是相同的,React 才能正确地将内部 state 和对应的 Hook 进行关联,它才能够正常工作。

备忘录Note
本站所提供的部分资源来自于网络,版权争议与本站无关,版权归原创者所有!仅限用于学习和研究目的,不得将上述内容资源用于商业或者非法用途,否则,一切后果请用户自负。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源。如果上述内容资对您的版权或者利益造成损害,请提供相应的资质证明,我们将于3个工作日内予以删除。本站不保证所提供下载的资源的准确性、安全性和完整性,源码仅供下载学习之用!如用于商业或者非法用途,与本站无关,一切后果请用户自负!本站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。如有侵权、不妥之处,请联系站长以便删除!
金点网络-全网资源,一网打尽 » Note·React Hook

常见问题FAQ

免费下载或者VIP会员专享资源能否直接商用?
本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。
是否提供免费更新服务?
持续更新,永久免费
是否经过安全检测?
安全无毒,放心食用
jamin

jamin 大神

上一篇
梁宁 增长思维30讲
下一篇
730份折纸教程打包

相关推荐

Note·Use a Render Prop!

Note·Use a Render Prop!

Note·React 和 Vue 中 key 的作用

Note·React 和 Vue 中 key 的作用

Note·TypeScript 中 interface 和 type 区别

Note·TypeScript 中 interface 和 type 区别

Note·TypeScript 中 is 关键字

Note·TypeScript 中 is 关键字

Note·Fetch data with React Hooks

Note·Fetch data with React Hooks

标签云
Android Atom ExecutorService ForkJoin GM GM后台 GM授权后台 H5 Java Javascript Linux手工服务端 pipbestcom Python ReentrantLock synchronized ThreadLocal volatile Win一键即玩服务端 一键端 传奇 写作 创业 单机 后台 商业端 外网 安卓 安卓苹果双端 工具 手工端 手游 搭建教程 教程 数据分析 文案 游戏源码 端游 经典 网单 职场 自媒体 视频教程 详细搭建教程 运营后台 页游

近期文章

  • 回合手游【逍遥西游之繁华西游】最新整理单机一键既玩镜像服务端_Linux手工端_GM后台_教程
  • 最新整理精品回合制手游【天书奇谈3D混沌完整版】VM一键单机版_linux手工外网端_隐盟视频教程_授权GM后台_双端
  • 典藏修真页游【诸仙列传OL】最新整理Win系服务端_GM工具_详细外网搭建教程
  • MT3换皮MH【浮生若梦尊享挂机修复版】最新整理单机一键即玩镜像端_Linux手工服务端_安卓苹果双端_GM后台_详细搭建教程
  • 大话回合手游【最新引擎之缥缈西游渡劫版】最新整理Linux手工服务端_安卓苹果双端_管理后台_CDK后台_详细搭建教程_视频教程

分类

  • | wordpress插件 |
  • | wordpress模板 |
  • | 其它模板 |
  • | 帝国模板 |
  • | 织梦插件 |
  • | 织梦模板 |
  • A5源码
  • Android软件
  • APP引流
  • E语言
  • H5
  • LUA
  • QQ营销
  • SEO推广
  • Windows软件
  • 体育运动
  • 信息数据
  • 创业专题
  • 办公教程
  • 口才演讲
  • 名师讲座
  • 商城/淘客/交易
  • 小吃技术
  • 小说/漫画/阅读
  • 建站教程
  • 引流脚本
  • 影视/音乐/视频
  • 影视资源
  • 微信/微商/微擎
  • 微信小程序
  • 微信营销
  • 微擎模块
  • 手游源码
  • 技能学习
  • 抖音课程
  • 摄影教程
  • 棋牌教程
  • 模板/主题/插件
  • 游戏源码
  • 爆粉引流
  • 理财/金融/货币
  • 生活老师
  • 电商客
  • 电子文档
  • 电脑教程
  • 社群营销
  • 站长工具
  • 精品网单
  • 系统工具
  • 素材资料
  • 综合资料
  • 编程经验
  • 网站源码
  • 网络安全
  • 网赚教程
  • 网赚源码
  • 考研资料
  • 脚本/AI/智能
  • 自媒体
  • 英语学习
  • 营销软件
  • 设计素材
  • 说书小说
  • 贴吧引流
  • 软件工具
  • 软文营销
  • 逆向软件
  • 音频讲座
  • 页游源码

提供最优质的资源集合

立即加入 友好社区
金点网络-全网资源,一网打尽

新一代全网资源综合门户网(www.pipbest.com-金点网络)专注服务于互联网,提供各类最新最全的免费源码下载(PHP、ASP、JSP、.NET),更提供免费工具,免费源码下载,软件下载,素材下载,赚钱教程下载,交流论坛等网站运营相关的一切内容,为网友搜罗最有价值的网站源码下载与技术教程等服务!

服务目录
  • 金点OpenAPI
  • 金点云
  • 金点支付
友情链接
  • 数媒派
  • 国家电网
快速搜索

本站由Nice强力驱动

声明: 本站部分内容属于原创转载请注明出处 如有侵权行为请严格参照本站【版权声明】与我们联系,我们将在48小时内容进行处理!

本站部分内容属于原创转载请注明出处 如有侵权行为请严格参照本站【版权声明】与我们联系,我们将在48小时内容进行处理!
© 2016-2023 PipBest.Com - 金点网络 & 金点部落. All rights reserved 京ICP备2022005359号-1
  • 关注有礼
  • 签到
  • 客服
    官方QQ群 常见问题 FAQ

    在线客服

    点我联系

    直接说出您的需求!
    切记!带上资源连接与问题!

    工作时间: 9:30-21:30

  • 暗黑
    模式
  • 全屏
  • 投稿
    赚钱
  • 首页

  • 签到

  • 切换

  • 客服

金点网络-全网资源,一网打尽
  • 登录
  • 注册
or
or
忘记密码?
金点网络-全网资源,一网打尽
  • 网站首页 ►
    • 金点部落
    • 小游戏
    • OpenAPI
    • 设计资产导航
    • 升级会员
  • 技能学习 ►
    • 体育运动
    • 办公教程
    • 口才演讲
    • 小吃技术
    • 建站教程
    • 摄影教程
    • 棋牌教程
    • 网赚教程 ►
      • 爆粉引流
      • 自媒体
      • 贴吧引流
  • 网站源码 ►
    • 商城/淘客/交易
    • 小说/漫画/阅读
    • 影视/音乐/视频
    • 微信/微商/微擎
    • 理财/金融/货币
    • 模板/主题/插件
  • 游戏源码 ►
    • 精品网单
    • 端游源码
    • 手游源码
    • 页游源码
  • 素材资料 ►
    • 电子文档
    • 综合资料
    • 考研资料
    • 设计素材
    • 音频讲座 ►
      • 人文艺术
      • 名师讲座
      • 说书小说
  • 软件工具 ►
    • Windows软件
    • MacOS软件
    • Android软件
  • 寄售资源
    ►
    • 游戏源码
    • 网站源码
    • 软件源码
  • 公益服
×
u3** 刚刚下载了 爆款吸金文案训练

    全网资源·一网打尽

  • 金点出品,必属精品!
  • 发布原创内容,获取高额提成!
  • 我们与你共创美好数字生态!
  • 无特殊说明密码默认:pipbest.com