最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 写给 Vue 开发的 React 上手指南

    正文概述 掘金(Kagashino)   2021-04-20   484

    写给 Vue 开发的 React 上手指南

    前言

    近年来前端框架 Angular、React 和 Vue 成为前端开发的主流,他们相比于 jQuery 封装了底层 DOM 操作,使开发者能够专注于业务数据,提升了开发体验。其中 React 和 Vue 的设计思路有异曲同工之妙,作为双修玩家,想写点东西用来沉淀自己的对于单项数据流、函数式组件和虚拟 DOM 等概念。

    本文适合以下类型的读者:

    • 被上司逼着换框架的 Vue 程序猿
    • 精力旺盛想尝试 React 的 Vue 程序猿
    • Vue 、 React 浅度使用,想深入了解其中一项的程序猿

    阅读本文需要掌握以下知识:

    • 理解 this 指向
    • 理解 ES6 的 class 、 解构赋值和箭头函数等语法

    另外,如果觉得看文档更加方便,这里提供传送门: reactjs.org/docs/gettin…

    React 简介

    来看看官网如何用一句话介绍 React:

    官方把 React 定义为一个 “UI 库” 而非框架,意思是 React 专注于使用 JavaScript 渲染用户界面(跟 UI 无关的东西不是我的本行) 库也好,框架也罢,能实现功能就是好东西,下面开始对比

    直观对比

    Talk is cheap, show you the code 让我们分别用两种框架实现一个购物车展示列表
    使用 Vue 可以这么写:

    <template>
      <div>
        <h3>{{ title }}</h3>
        <ul>
          <li v-for="i in cart" :key="i.name">
            <span>{{ i.name }} - {{ i.price }}</span>
          </li>
        </ul>
        <p>总价: {{ total }}</p>
        <button @click="settle">结账</button>
      </div>
    </template>
    <script>
    export default {
      props: {
        title: String
      },
      data() {
        return {
          cart: [
                {name: '牙膏', price: 6.5},
                {name: '香皂', price: 16},
                {name: '洗发水', price: 19.5},
          ]
        }
      },
      computed: {
        total() {
          return this.cart.reduce(( prev, curr ) => prev + curr.price, 0)
        }
      },
      methods: {
        settle() {
          alert(`下单成功,总价: ${this.total}`)
        }
      },
    }
    </script>
    

    emmm,数据视图分离、data、computed ..., 多么熟悉的画面,把这段逻辑挪到 React 中,它长这样:

    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    
    export default class Cart extends Component {
      // 属性类型检查
      static propTypes = {
        title: PropTypes.string;
      }
      // 组件的状态
      state = {
        cart: [
          { name: '牙膏', price: 6.5 },
          { name: '香皂', price: 16 },
          { name: '洗发水', price: 19.5 },
        ]
      }
      // 计算总价
      total = () => this.state.cart.reduce((prev, curr) => prev + curr.price, 0)
      // 结账回调
      settle = () => {
        alert(`下单成功,总价: ${this.total()}`)
      }
    
      // 渲染视图
      render() {
        const {
          props: { title },
          state: { cart },
          total,
          settle
        } = this;
    
        // JSX 模板
        return (
          <div>
            <h3>{title}</h3>
            <ul>
              {
                cart.map((i) => (
                  <li key={i.name}>
                    <span>{i.name} - {i.price}</span>
                  </li>
                ))
              }
            </ul>
            <p>总价: {total}</p>
            <button onClick={settle}>结账</button>
          </div>
        )
      }
    }
    

    hmmm, 除了 JSX (见下文),其他都是纯粹的 JavaScript,通过以上代码的对比,我们发现了几个信息:

    1. 两个框架中模板、数据和回调这几个要素非常相似: render 函数对应 Vue 的 Template (提一嘴, Vue 也可以使用 JSX 编写 render 函数); state 对应 Vue 中的 data 表示组件的状态; 而 props 意思相同,为父组件传下来的数据。
    2. 从写法上来说,React 偏 JavaScript ,Vue 则是 HTML 与 JavaScript 的结合,内置许多指令属性。
    3. React 使用了 JSX 作为模板, JSX 语法相对比较简单,基本就是元素</> + 表达式{},相比 Vue 中 Template 集中编写,JSX 可以写得比较松散。

    如果你倾向于 All in JavaScript ,追求代码的灵活度,那么 React 可能和你更有缘分。

    详细对比

    上文说到 React 相对灵活,但灵活是把双刃剑,使用不当容易掉进坑里。尽管 React 社区生态非常丰富,可以帮你解决绝大部分问题,但有些东西和 Vue 不太一样,刚上手的人要留意。下面我将罗列几个与 Vue 不相同的地方:

    创建虚拟 DOM —— JSX vs Template

    虚拟 DOM 的本质是 JavaScript 对象,使用 JavaScript 对象来描述真实 DOM,在数据变化更新时可以进行一定程度得优化,避免真实 DOM 的冗余更新。我们创建 React 的虚拟 DOM ,如果使用原生 JavaScript 会写成:

    const myButton = React.createElement(
        'button', 
        { onClick: () => doSth() }, 
        'Hello World'
    )
    

    这么写很难让人看出来是在创建一个 HTML 元素。
    JSX 是一种 JavaScript 的语法扩展,借助 JSX 我们可以把上面的 button 元素写得更像 HTML:

    const myButton = (
        <button onClick={() => doSth()}>
            Hello World
        </button>
    )
    

    相比于 Template ,JSX 就显得比较简单纯粹了,在 Template 中可以使用各种便利的指令,而在 JSX 中只有属性 prop,没有指令这个概念,来看看具体有何不同:

    表单绑定

    Tempalte 中一条 v-model 搞定

    <input v-model="value"/>
    

    JSX 中,老老实实展开来写吧

    <input value={value} onChange={onChange}/>
    

    条件渲染

    <div v-if="show">Should I render?</div>
    

    JSX 中花括号 {} 内可以写三目表达式,用它来替代条件渲染

    {
      show ? <div>Should I render?</div> : null
    }
    

    或者封装一个 <If /> 高阶函数(似乎这么做的人不多):

    const If = (props) => props.condition ? props.children : null;
    
    <If condition={show}>
      <div>Should I render ?</div>
    </If>
    

    props.children 为子节点渲染,参考下文

    子节点渲染

    Template 中需要使用 slot 作为占位:

    <div> this is default <slot></slot> </div>
    <div> this is named <slot name="bar"></slot><div>
    

    父节点传入:

    <Foo>
        <span> child node </span>
    </Foo>
    
    <Foo>
        <span slot="bar"> bar node </span>
    </Foo>
    

    JSX 中直接将节点当作 prop 传入即可,默认子节点会被转换成 children prop

    <div> this will render { props.children } </div>
    <div> this will render { props.whatever } </div>
    

    父节点传入

    <Foo>
        <span> child node </span>
    </Foo>
    
    <Foo whatever={<div>text, nodes or any valid element</div>} />
    

    列表渲染

    为了优化列表更新的性能,我们在每个元素上声明一个 key 属性,这一点两家都一样:

    <ul>
        <li v-for="i in list" :key="i.id">{i.name}</li>
    </ul>
    

    JSX 中因为不能使用 for 语句,得利用 Array map 进行渲染:

    <ul>
        { list.map(i => <li key={i.id}>{i.name}</li>) }
    </ul>
    

    碎片渲染

    声明 JSX 节点,根元素必须唯一,像以下的写法是不行的:

    const badNode = (
        <div>1</div>
        <div>2</div>
    )
    

    如果不希望添加多余的 DOM 元素,可以使用 React 碎片

    const frag = (
      <React.Fragment>
        <div>1</div>
        <div>2</div>
      </React.Fragment>
    )
    
    //  或者简写成:
    const frag = (
      <>
        <div>1</div>
        <div>2</div>
      </>
    )
    

    在 Vue 中 Template 的使用相对集中,碰到碎片化渲染的场景不多,但也有类似的场景,比如对一组元素做循环:

    <template v-for="i in item" :key="i.id">
      <h3>{{i.title}}</h3>
      <p>{{i.name}}</p>
    </template>
    

    一言难尽的 this

    这个是 JavaScript 的问题,框架不背锅。Vue 中,不管是在 datacomputed / watch 还是 methods 中,this 始终指向当前组件,这是因为 Vue 在创建组件的时候帮你做了一次绑定。 而 React 就没那么贴心了(灵活嘛),这里有一段代码:

    class Comp extends Component {
      state = {
        a: 1
      }
    
      printA() {
        console.log(this.state.a); // "this" is undefined
      }
    
      render() {
        return (
          <button onClick={this.printA}>Click</button>
        )
      }
    }
    

    React 的行家一眼就能发现问题: 当点击 button 的时候,printA 方法中的 this 并不是指向当前组件,所以执行 printA 会报错。为了保证 this 指向,你可以从以下三个选项中挑一个来解决:

    • 解法一:使用箭头函数(推荐) 利用箭头函数绑定上一层 this 的原理,可以将成员函数转成箭头函数:
    class Comp extends Component {
      printA = () => {
        console.log(this.state.a);
      }
    }
    

    或者在 render 函数一头处理,不过每次重渲染都会创建一个新的箭头函数:

    <button onClick="() => this.printA()">Click</button>
    

    这种解法在编写 class 组件时心智负担相对比较小。

    • 解法二: 使用 bind 改变 this 指向,和上面的问题一样:每次重渲染都会创建一个新的函数:
    <button onClick="this.printA.bind(this)">Click</button>
    

    或者在 constructor 中处理,保证每次引用一样

    class Comp extends Component {
      constructor(props) {
        super(props);
        this.printA = this.printA.bind(this);
      }
    }
    
    • 解法三:使用函数组件(推荐)

    既然 this 这么麻烦,咱不用 class 组件了可好?答案是可行的,React 16.8 之后开始支持 React Hooks ,可以使用函数组件替代 class 组件,不用再纠结 this 了!参见下文 React Hooks

    Immutable 不可变特性

    第一次看到这个概念,本人也很懵:状态时刻都在变化,为什么说不可变呢? 这里的不可变,是函数式编程的一个理念(过于深入,不做讲解):假设把 React 组件比作函数,那么每次更新组件都不要直接修改 state 里的数据,而是生成一个新的 state 来替换。我们在 Vue 中习惯了响应式更新,修改状态时直接赋值:

    this.state.cart[1].price = 5.5;
    this.state.cart.push({ name: '毛巾', price: 32.9 })
    

    React 告诉你: Don't do that! 我为你提供了 setState 方法,调了我的 setState ,并且新值和旧值不相等(Object.is(oldVal, newVal))我才认为你修改了状态:

    // 修改第一项的 price
    this.setState({
      cart: this.state.cart.map((item, index) => index === 1 ? { ...item, price: 5.5 } : item)
    })
    
    // 添加一个商品
    this.setState({
      cart: [...this.state.cart, { name: '毛巾', price: 32.9 }]
    })
    

    可以看到,除了调用 setState 方法,新数据与原数据的引用是不一样的,为的是让 React 比对旧值是发现数据的变化,从而触发更新。

    比起 JS 引擎创建对象这种开销,不如关心一下你的 JS 文件体积过大影响加载以及 DOM 更新的粒度?

    单向数据流

    这个简单,子组件不要直接修改父组件的状态:

    function MyInput({ value, onChange }) {
      return (
        <input
          placeHolder="type something"
          value={value}
          onChange={e => onChange(e.target.value)}/>
      )
    }
    

    这一点上, Vue 的一个折中的办法是:把 value 和 onChange 封装成 v-model (抱歉,template 编译就是可以为所欲为),但原理是一样的:

    <template>
      <input placeholder="type something" value="value" @change="e => onChange(e.target.value)" />
    </template>
    <script>
      export default {
        model: {
          value: 'value',
          event: 'change'
      }
    }
    </script>
    

    生命周期钩子

    组件不是一个持久化的东西,从创建、更新到销毁,每一个 timing 都可以处理一些逻辑:

    • constructor: 对应 Vue 中的 data 函数(想必你们都知道 Vue 的 data 得写成一个函数吧),我想来想去,把它与 data 对比最合适,因为在 constructor 中可以初始化组件的 state 。
    • componentDidMount: 对应 Vue 中的 mounted,第一次渲染时调用,可以在该函数内发起初始化异步请求什么的。
    • componentDidUpdate: 对应 Vue 中的 updated,组件更新(除了第一次渲染)时调用,处理 state 和 props 更新后的逻辑。
    • componentWillUnmount: 对应 Vue 中的 beforeDestroy,组件卸载之前调用,一般用来取消定时器、退订事件等。
    • shouldComponentUpdate: React 独有,根据传入的 props、state 的变化,返回 true 或者 false 来决定组件是否更新,有利于减少重渲染带来的性能开销和副作用。

    剩下一些不常用的,可以移步文档 reactjs.org/docs/react-…

    React 生命周期函数名称都比较长,不是什么技术原因,主要是为了保持一个提醒的作用:哦,原来这里用了这么个钩子。

    特性

    讲完了对比,接下来介绍一些 React 的优势项

    Typescript 支持

    由于 React 偏 JavaScript ,对 TypeScript 的支持是强于 Vue2.x 的(Vue 在 3.x 以上版本对 TS 支持有所改进),如果你理解静态类型检查对于复杂前端工程的意义,那么 React 是一个非常合适的选择。让我们使用 TypeScript 对 React Class 组件进行类型标注:

    import React, { Component } from 'react';
    
    interface IProp {
      title: string;
    }
    
    interface IState {
      foo: number;
      bar: string;
      baz: string[];
    }
    
    // 第一个泛型表示组件中有哪些 Props , 第二个表示组件的 State
    class Comp extends Component<IProp, IState> {
        state = {
            foo: 0,
            bar: 'bar',
            baz: ['ak', 'm16', 'famas']
        }
    }
    

    有了静态类型除了能够对代码加强约束,还能让 IDE 放心地推导类型,给予合适的代码补全,可谓一次编写,处处受益。有关 TypeScript 支持请参考 zh-hans.reactjs.org/docs/static… 。

    React Hooks —— 函数式组件补完计划

    事实上,除了上文使用 class 创建组件,也可以使用函数创建,并且 React 团队鼓励大家使用函数编写组件,继续折腾购物车,这回使用函数来组织代码:

    import React from 'react';
    
    const defaultCart = [
      { name: '牙膏', price: 6.5 },
      { name: '香皂', price: 16 },
      { name: '洗发水', price: 19.5 },
    ]
    
    // 第一个参数为函数组件的 prop
    function Cart({ title, cart = defaultCart }) {
    
      const total = cart.reduce((prev, curr) => prev + curr.price, 0);
    
      const settle = () => {
        alert(`下单成功,总价: ${total}`)
      }
    
      return (
        <div>
          <h3>{title}</h3>
          <ul>
            {
              cart.map((i) => (
                <li key={i.name}>
                  <span>{i.name} - {i.price}</span>
                </li>
              ))
            }
          </ul>
          <p>总价: {total}</p>
          <button onClick={settle}>结账</button>
        </div>
      );
    }
    

    函数组件当中,第一个参数为父组件传入的 props, 问题来了:state 怎么办?目前我们的函数组件仅支持静态渲染,如果需要修改购物车的状态,不得不再引入一个 onChange 的 prop ,由父组件修改。有没有方法能让函数拥有自己的 state 呢?

    答案是使用 React Hooks,它们是 React 内置的一组钩子函数(跟生命周期钩子有所区别,可以从零开始理解),函数名称以 use 起头。能够将状态与当前渲染的函数组件“关联”起来,且看:

    import React, { useState } from 'react';
    
    const Cart = ({ title }) => {
      // 状态:购物车清单
      const [cart, setCart] = useState([
        { name: "牙膏", price: 6.5 },
        { name: "香皂", price: 16 },
        { name: "洗发水", price: 19.5 }
      ]);
      // 状态:新增货品名称
      const [name, setName] = useState("");
      // 状态:新增货品价格
      const [price, setPrice] = useState();
    
      const addItem = () => {
        setCart([...cart, { name, price }]);
      };
    
      const clearAll = () => {
        if (confirm("是否删除购物车里的商品?")) {
          setCart([]);
        }
      };
    
      const total = cart.reduce((prev, curr) => +prev + +curr.price, 0);
    
      const settle = () => {
        alert(`下单成功,总价: ${total}`);
      };
    
      return (
        <div>
          <h3>{title}</h3>
          <ul>
            {cart.map((i) => (
              <li key={i.name}>
                <span>
                  {i.name} - {i.price}
                </span>
              </li>
            ))}
          </ul>
          <div>
            <span>
              商品名称:
              <input
                type="text"
                value={name}
                onChange={(e) => setName(e.target.value)}
              />
            </span>
            <span>
              价格:
              <input
                type="number"
                value={price}
                onChange={(e) => setPrice(+e.target.value)}
              />
            </span>
            <button onClick={addItem}>添加商品</button>
          </div>
          <p>总价: {total}</p>
          <button onClick={settle}>结账</button>
          <button onClick={clearAll}>删除</button>
        </div>
      );
    };
    

    我们从 react 中引入了 useState 钩子,这可能是编写函数组件最常用的钩子了——它的参数接受一个默认值,返回 [value, setValue] 的元组(就是约定好值的 JavaScript 数组啦),来帮助我们读取和修改数据。 有了 useState,我们终于让函数组件发挥出 class 组件的功能。

    React Hooks 注意事项

    为了保证函数组件知道它自己调用了什么钩子,React Hooks 被设计成执行顺序和组件渲染顺序一致(有兴趣的可以找专门的 React Hooks 文章了解),数据结构可以看作是一个链表:

    为了保证 Hooks 调用顺序一致,下面几个用法都应该避免:

    • 不要在 if / while / for 等流程语句中使用 Hooks
    // bad 
    if (someThing) {
      useState(a)
    } else {
      useState(b)
    }
    
    • 不要在函数组件执行域之外调用 Hooks
    // bad, do not use outside the functional component
    const [outer, setOuter] = useState()
    
    // bad, do not use inside class component
    class A extends Component {
      constructor () {
        this.state = useState()
      }
    }
    

    其他 Hooks

    除了 useState , React 还内置了如 useEffectuseLayoutEffectuseRef useMemo 等钩子。这里介绍 useEffectuseMemouseCallback 这三个带依赖项(参考下文 QA 解释)的钩子,剩下的参考文档 reactjs.org/docs/hooks-…

    useEffect

    这是个多面手,处理组件更新后副作用的回调,当 state 或者 props 改变引发函数重新渲染时,根据参数和返回值的定义选择性执行。大致可以驾驭以下场景:

    • componentDidMountcomponentDidUpdate 的合体,只要重新渲染始终会执行:
    // 只传一个回调,函数重新渲染就执行
    useEffect(() => { console.log('updated') })
    
    • 指定某些依赖作为数组传入第二个参数,只要有一项依赖发生变化就执行,类似 Vue 中的 watch
    function Comp({ title }) {
      const [count, setCount] = useState(0);
      // 第二个参数指定一个数组,放入你想监听的依赖:
      useEffect(() => {
        console.log('title or count has changed.')
      }, [title, count])
    }
    

    原则上,函数中用到的所有依赖都应该放进数组里。

    • 处理组件第一次渲染时的回调,类似 Vue 中的 mounted
    // 第二个参数传一个空数组,表示没有依赖,只会在第一次渲染时执行
    useEffect(() => {
      alert('mounted');
    }, [])
    
    • 处理首次渲染和销毁,类似 Vue 中 mountedbeforeDestroy 组合
    useEffect(() => {
        const onKeyPress = e => console.log(`You've press ${e.key} .`);
        document.addEventListener('keyPress', onKeyPress);
        // 如果返回值是函数,组件销毁之前 React 会调用这个函数来清理副作用
        return () => document.removeEventListener('keyPress', onKeyPress);
    }, [])
    
    • 注意:Effect 钩子的依赖使用不当会导致组件无限更新(死循环):
    // 1. 依赖一个 state 又在 effect 中更新这个 state
    function Bad1() {
      const [count, setCount] = useState(0);
      useEffect(() => {
        setCount(count++);
      }, [count])
    }
    
    // 2. 每次依赖对象引用不一致
    function Bad2() {
      // 组件重新渲染时 data 都是一个新对象。
      const data = { a: 1, b: 2 }
      useEffect(() => {
        console.log(data);
      }, [data])
    }
    
    useMemo/useCallback

    useMemo 类似于 Vue 的 computed,表示一个记忆状态 接受一个求值函数和一串依赖数组:

    用法

    const [name, setName] = useState('John Smith');
    const [age, setAge] = useState(17);
    
    const getData = () => ({ name, age });
    const data = useMemo(getData, [name, age])
    

    第一次渲染时调用 getData 取值不必多说。当组件重新渲染时,如果元组中 name、age 其中一项变化,则仍会调用getData函数重新求值。如果二者都没变化,直接返回上一次渲染的数据给 data,避免了引用类型变化问题。

    useCallback 是 useMemo 的函数版,防止组件重渲染时回调引用被覆盖:

    const [count, setCount] = useState(0);
    const setCount = useCallback(() => setCount(count + 1) , [count]);
    

    由于函数闭包特性,依赖项count必须传入,如果不传则每次都只会得到初次渲染的值0

    一句话概括 useEffect, useMemouseCallback:

    自己封装

    将多个 hooks 封装进一个函数,再在函数组件中调用是允许的,因为这样不会破坏执行顺序:

    const useCount = () => {
      const [count, setCount] = useState(0);
      const text = useMemo(() => `You've click ${count} times!`);
      const add = useCallback(() => setCount(count + 1));
      const reset = useCallback(() => setCount(0));
      return {
        text,
        add,
        reset
      }
    }
    
    const Comp = () => {
      const { text, add, reset } = useCount();
    }
    

    不过也要注意:封装的钩子函数名称也要用 use 开头(这是一个约束)。

    小结

    React 和 Vue 是目前前端流行的三大框架之二。本文以对比的视角,通过单文件组件、JSX 和 Template 声明周期等各自特性的对比,介绍了 React 的基本写法和注意事项。为的是让大家更清晰地认识两者之间的区别和相似点,加深对这两者的理解。

    问答环节

    刚才在 React Hooks 中提到的依赖是啥

    函数式组件,每一次更新都会重新执行一遍函数,所以将 propstatememocontextcallback可能会随着函数重新渲染而变的值叫做依赖。而 refuseState 中返回的 setXXX 函数,不会随着组件重新渲染而改变,所以不算做依赖。

    setState 到底是同步还是异步

    我觉得不应该过多关心这个问题,但鉴于许多面试都会问到这个,在此梳理一下:

    1. 首先 setState 更新是同步的,但是调用完成后不会直接赋值给 state ,所以造成了调用后 state 没有立即变化的情况,但不是异步逻辑造成的。
    2. 在同步的回调、componentDidMount 等钩子中,因为批处理机制的存在,必须要等到所有 setState 调用完毕,才会一次性更新:
    class A extends Component {
      state = { a: 1 }
    
      callSync() {
        this.setState({ a: 100 });
        console.log(this.state.a); // 1
        this.setState({ a: 200 });
        console.log(this.state.a); // 1
        this.setState({ a: 300 }, () => console.log(this.state.a)); // 300
      }
    }
    
    1. 在 setTimeout 、 Promise 等异步代码中,由于批处理流程已经结束了,所以 setState 会立即更新
    class A extends Component {
      callAfterTimeout = () => {
        setTimeout(() => {
          this.setState({ a: 100 });
          console.log(this.state.a) // 100
        }, 0)
      }
    
      callAfterPromise = () => {
        Promise
          .resolve()
          .then(() => {
            this.setState({ a: 100 });
            console.log(this.state.a) // 100
          })
      }
    
      callAfterAwait = async () => {
        await Promise.resolve();
        this.setState({ a: 100 });
        console.log(this.state.a) // 100
      }
    }
    

    能不能封装一个 v-model

    借助 React Hooks 来做一层封装(花里胡哨的玩意,似乎这么做的人不多):

    const useModel = ({ value = 'value', event = 'onChange' }, defaultValue) => {
      const [val, setVal] = useState(defaultValue);
      return {
        [value]: val,
        [event]: useCallback(e => setVal(e.target[value]), [])
      }
    }
    
    const MyInput = () => {
      const inputModel = useModel(); // { value: '', onChange: f }
      const checkboxModel = useModel({ value: 'checked' }, true); // { checked: true, onChange: f }
    
      return (
        <form>
          <input {...inputModel} />
          <input type="checkbox" {...checkboxModel} />
        </form>
      )
    }
    

    JSX 不是 React 专用吗? Vue 也能写 JSX ?

    1. 不是,2. 能 cn.vuejs.org/v2/guide/re…

    有计划出镜像文章:写给 React 开发的 Vue 上手指南吗?

    你好,有的

    有计划带上 Angular 吗?

    抱歉,Angular 用的不多,暂不编写。


    起源地下载网 » 写给 Vue 开发的 React 上手指南

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元