jsliang 求职系列 - 38 - React 系列总结

一 目录

不折腾的前端,和咸鱼有什么区别html

目录
一 目录
二 前言
三 知识点
四 前端历史演进
五 React 相比原生的好处
六 React 和 Vue 比对
6.1 相同之处
6.2 不一样之处
七 React Fiber
八 React 生命周期
8.1 版本 以前
8.2 版本 以后
  8.2.1 挂载阶段
  8.2.2 更新阶段
  8.2.3 卸载阶段
九 setState
9.1 调用 setState 以后发生了什么?
9.2 setState 是同步仍是异步?
十 React this 问题
十一 受控组件和非受控组件
十二 组件通信
十三 Redux
十四 Mixin、HOC 和 Hook
14.1 Mixin
14.2 高阶组件(HOC)
14.3 Hook
十五 性能优化
十六 参看文献
16.1 面试知识点
16.2 系统
16.3 React 和 Vue 比对
16.4 生命周期
16.5 受控组件和非受控组件
16.6 Diff 和 虚拟 DOM
16.7 React 源码
16.8 React Mixin
16.9 React Hoc
16.10 React Hooks
16.11 React Fiber
16.12 服务端渲染(SSR)
16.13 性能优化
16.14 其余

二 前言

返回目录

React 是现现在流行的前端框架,也是不少大厂面试必备。前端

React 与 Vue 虽有不一样,但一样做为一款 MV* 框架,虽然实现可能不同,但在一些理念上仍是有类似的,例如数据驱动、组件化、虚拟 DOM 等。vue

固然,还有一些问题,可能最近都无法搞清楚的了,毕竟罗马不是一天能建成的:react

  • [ ] React 的实现原理?有什么优缺点?

这些每每要更深刻挖掘方可得知本身的结论,任重道远,不停歇。webpack

三 知识点

返回目录
  • [x] 前端历史演进
  • [x] React 和原生对比git

    • [x] 组件化
    • [x] 自然分层
    • [x] 生态好
    • [x] 开发效率
  • [x] React 和 Vue 对比github

    • [x] 相同之处:虚拟 DOM、组件化、构建工具、配套框架、Chrome 开发工具
    • [x] 不一样之处:模板 和 JSX、监听数据变化方式不一样、Diff 不一样
  • [x] React Fiber
  • [x] React 生命周期web

    • [x] 挂载阶段:constructorgetDerivedStateFromPropsrendercomponentDidMount
    • [x] 更新阶段:getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate
    • [x] 卸载阶段:componentWillUnmount
  • [x] setState面试

    • [x] 调用以后发生什么
    • [x] 同步仍是异步
  • [x] this 指向问题算法

    • [x] 经过 bind 修正
    • [x] 经过箭头函数修正
    • [x] bind 和箭头函数的区别
  • [x] 受控组件和非受控组件:valuedefaultValue
  • [x] 组件通信

    • [x] props
    • [x] Context
    • [x] Redux
  • [x] ReduxReduxReact-Redux 以及 Redux-Saga 工做流
  • [x] MixinHOCHook
  • [x] 性能优化

    • [x] 首屏渲染优化 prerender-spa-plugin
    • [x] 页面占位 react-placeholder
    • [x] 页面切换优化 html-webpack-plugin
    • [x] 减小业务体积代码 Tree Shaking
    • [x] 提取公共代码 SplitChunkPlugin
    • [x] 切分代码 Code Splitting
    • [x] 懒加载 react-lazyload

四 前端历史演进

返回目录
  • jQuery 时代

经过 Ajax 从后端获取数据,而后经过 jQuery 生成 DOM 结果更新到页面中。

可是随着业务发展,项目愈来愈复杂,交互性愈来愈强,每每用户在某个时刻可能操做好几块内容,从而 DOM 的操做愈来愈频繁,页面性能逐步下降,用户也不满意这样卡慢的现状了。

  • MVVM

这时候有了 MVVM,双向数据绑定让数据在修改的时候同步 DOM 的更新,反之亦可。

这个设定大大下降手动维护 DOM 的成本,而 MVVM 为 React 的特性之一,虽然 React 属于单项数据流,须要咱们手动实现双向数据绑定。

  • 虚拟 DOM

光靠绑定是不够的,这样无法解决频繁操做 DOM 的问题。

因此 React 内部实现了一套虚拟 DOM 的更新,它将真实 DOM 在 JS 中作一套缓存,每次有数据更新的时候,先内部经过 Diff 算法进行比对,而后收集一箩筐更新后,才对 DOM 进行更新,这样就大大下降了 DOM 的操做次数。

  • Diff 运做

那么,Diff 怎么运做呢?

Diff 获取虚拟 DOM 节点变动的 4 种状况比较:节点类型变了、节点类型同样,仅仅属性或者属性值变了、文本变了、增长、删除或者移动了子节点。

  • setState

React 不一样于 Vue,能够经过 v-model 的形式,让用户的操做和 JavaScript 存储的数据同步更新,它须要经过 setState 来更新组件内容。

  • Redux

可是,若是想经过一个组件来渲染它兄弟组件,React 一开始在这块作得并非那么好,因此就须要引入一个状态管理中心,来帮助咱们管理状态(state),于是就有了 Redux

Redux 中,当 state 有变化的时候,依赖这个 state 的组件就会从新渲染,这样就解决了组件间数据传递的问题。

  • Mobx

Redux 有个问题,就是修改某个 state 的时候,须要通过 action.jstypes.jsreducers.js 这一系列文件,这样子 Redux 的数据流虽然很是正规,可是写起来复杂啊。

因此,社区又出现了另外一套解决方案,也就是 Mobx

Mobx 推崇代码简约移动,只须要定义一个可贯彻的对象,而后在哪一个组件中使用到了这个可观察对象,而且这个对象的数据有更改,那就会从新渲染,这使得 Mobx 开发项目的时候能够简单快速地完成不少功能。

可是 Mobx 也有缺点,就是数据流太过随意,出了 Bug 很差定位。

  • End

因此,针对于小项目来讲,社区推荐使用 MobX,对大项目推荐使用 Redux。

本段文本参考的内容已记录在参考文献中

五 React 相比原生的好处

返回目录
  • 组件化: 其中以 React 的组件化最为完全,甚至能够到函数级别的原子组件,高度的组件化能够是咱们的工程易于维护、易于组合拓展。
  • 自然分层: JQuery 时代的代码大部分状况下是面条代码,耦合严重,现代框架无论是 MVC、MVP 仍是 MVVM 模式都能帮助咱们进行分层,代码解耦更易于读写。
  • 生态: 如今主流前端框架都自带生态,无论是数据流管理架构仍是 UI 库都有成熟的解决方案。
  • 开发效率: 现代前端框架都默认自动更新 DOM,而非咱们手动操做,解放了开发者的手动 DOM 成本,提升开发效率,从根本上解决了 UI 与状态同步问题.

六 React 和 Vue 比对

返回目录

6.1 相同之处

返回目录
  1. 虚拟 DOM。映射真实 DOM,经过新旧 DOM 的 diff 对比,更好的跟踪渲染页面。
  2. 组件化。将应用拆分红一个个功能明确的模块,每一个模块之间能够经过合适的方式互相联系。
  3. 构建工具。都有本身的构建工具,经过 Webpack + Babel 去搭建脚手架。
  4. Chrome 开发工具。二者都有很好的 Chrome 扩展去帮助查找 Bug。
  5. 配套框架。Vue 有 Vue-routerVuex,而 React 有 React-routerReact-Redux

6.2 不一样之处

返回目录
  1. 模板 VS JSX。Vue 推荐编写近似常规 HTML 的模板进行渲染,而 React 推荐 JSX 的书写方式。
  2. 监听数据变化的不一样。Vue 使用的是可变数据,而 React 更强调数据的不可变。在 Vue 中经过 v-model 绑定的数据,用户改变输入值后对应的值也相应改变。而 React 须要经过 setState 进行设置变化。
  3. Diff 不一样。Vue 经过双向链表实现边对比边更新 DOM,而 React 经过 Diff 队列保存须要更新的 DOM,获得 patch 树,再统一批量更新 DOM。
  4. 开发团队。Vue 一开始核心就是 Evan You,后面再招了其余人组成团队;React 的话是一开始就是 Facebook 团队搞的。因此网上的人比对源码状况的话,Vue 的比 React 的简单易懂点。
实话实说是看别人说的异同,担忧有 “小伙伴” 有本身观点,而后在这里被喷, jsliang 不敢持有任何本身的观点,可是面试我老是要这么回答面试官的 /滑稽

七 React Fiber

返回目录

React 的核心流程能够分为两个部分:

  • reconciliation (调度算法,也可称为 render):

    • 更新 stateprops
    • 调用生命周期钩子
    • 生成虚拟 DOM
    • 经过新旧 VDOM 进行 diff 算法,获取 VDOM change
    • 肯定是否须要从新渲染
  • commit

    • 如须要,则操做 DOM 节点更新;

为何须要 Fiber

随着应用变得愈来愈庞大,整个更新渲染的过程开始变得吃力,大量的组件渲染会致使主进程长时间被占用,致使一些动画或高频操做出现卡顿和掉帧的状况。

而关键点,即是 同步阻塞

在以前的调度算法中,React 须要实例化每一个类组件,生成一颗组件树,使用 同步递归 的方式进行遍历渲染,而这个过程最大的问题就是没法 暂停和恢复

因此,为了解决这个问题(同步阻塞),一般有两种方法: 异步任务分割

React Fiber 即是为了实现任务分割而诞生的。

React Fiber 简述:

  • 在 React 16 版本中将调度算法进行了重构, 将以前的 stack reconciler 重构成新版的 fiber reconciler,变成了具备链表和指针的 单链表树遍历算法。经过指针映射,每一个单元都记录着遍历当下的上一步与下一步,从而使遍历变得能够被暂停和重启。
  • 能够理解为是一种 任务分割调度算法,主要是将原先同步更新渲染的任务分割成一个个独立的 小任务单位,根据不一样的优先级,将小任务分散到浏览器的空闲时间执行,充分利用主进程的事件循环机制。

React Fiber 核心:

  • 能够具象为一个数据结构
  • 链表树遍历算法
  • 任务分割
  • 分散执行
  • 优先级策略

八 React 生命周期

返回目录

React 逐渐废弃的生命周期方法:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

8.1 版本 以前

返回目录

8.2 版本 以后

返回目录

8.2.1 挂载阶段

返回目录
  • constructor:构造函数,最早被执行,一般在构造函数中初始化 state 对象或者给自定义方法绑定 this
  • getDerivedStateFromPropsstatic getDerivedStateFromProps(nextProps, prevState),这是个静态方法,当咱们接收到新的属性想去修改咱们 state,可使用 getDerivedStateFromProps
  • renderrender 函数是个纯函数,只返回须要渲染的东西,不该该包含其余的业务逻辑,能够返回原生 DOM、React 组件、FragmentPortals、字符串和数字等内容。
  • componentDidMount:组件装载以后调用,此时咱们能够获取到 DOM 节点并操做,好比对 CanvasSVG 等操做。服务器请求、订阅均可以写这个里面,可是记得在 componentWillUnmount 中取消订阅。

React 的接口请求是放在 componentDidMount 里面比较合适,旧版本有人放在 componentWillMount 里面,从而致使屡次请求,如今 componentWillMount 不推荐使用了,因此转 componentDidMount 就很是科学了。

存在如下问题:

  • 为何 getDerivedStateFromProps 是静态的?

当它设置为静态函数,代表这个函数不能经过 this 访问到 class 的属性,也并不推荐直接访问属性。

  • 哪些生命周期能够 setState

能够在 componentDidMountcomponentDidUpdate 中使用,此时 DOM 已经稳定下来了,能够进行数据的操做了。

8.2.2 更新阶段

返回目录
  • getDerivedStateFromProps:此方法在更新阶段也会被调用。
  • shouldComponentUpdateshouldComponentUpdate(nextProps, nextState),有两个参数,表示新的属性和变化以后的 state,返回一个布尔值。若是是 true 表示会触发从新渲染,false 表示不会触发从新渲染,默认返回 true。能够利用这个生命周期来优化 React 程序性能。
  • render:同挂载阶段 render
  • getSnapshotBeforeUpdategetSnapshotBeforeUpdate(prevProps, prevState),这个方法会在 render 以后,componentDidUpdate 以前调用,有两个参数,表示以前属性和以前的 state。这个函数有一个返回值,会做为第三个参数传给 componentDidUpdate,若是不须要返回值,能够返回 null,这个方法必须和 componentDidUpdate 配合使用。
  • componentDidUpdatecomponentDidUpdate(prevProps, prevState, snapshot),在 getSnapshotBeforeUpdate
    以后调用,有三个参数,表示以前的 props,以前的 state,以及 snapshot。参数 snapshotgetSnapshotBeforeUpdate 返回的,若是触发某些回调函数时须要用到 DOM 元素的状态,则将对比或者计算过程迁移到 getSnapshotBeforeUpdate,而后在 componentDidUpdate 中统一触发回调或者更新状态。

8.2.3 卸载阶段

返回目录
  • componentWillUnmount:当组件被卸载或者销毁时会被调用,在这里清除定时器,或者取消网络请求,用来清理无效的 DOM 元素等垃圾回收工做。

九 setState

返回目录

setState 是 React 中用于修改状态,更新视图的方法。

9.1 调用 setState 以后发生了什么?

返回目录

在代码中调用 setState 以后,React 会将传入的参数对象与组件当前的状态合并,触发所谓的调和过程(Reconciliation)。

通过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树而且着手从新渲染整个 UI 界面。

在 React 获得元素树以后,React 会自动计算新树和老树之间的节点差别,而后根据差别对界面进行最小化从新渲染。

在差别计算算法(Diff)中,React 可以相对精确地知道哪些位置发生了改变以及英国如何改变,保证了按需更新,而不是所有从新渲染。

简单来讲:

  1. 合并参数对象,触发调和过程
  2. 计算新树和老树差别(Diff
  3. 根据差别进行最小化从新渲染

9.2 setState 是同步仍是异步?

返回目录

回答:有时候同步,有时候异步。

  1. setState 在合成事件和钩子函数中是异步的,在原生事件和 setTimeout 是同步的。
  2. setState 的异步,并非说内部由异步代码实现,它自己执行的过程和代码是同步的,只是合成事件和钩子函数的调用顺序在更新以前,致使在合成事件和钩子函数中无法立马拿到更新后的值,从而造成了所谓的异步。
  3. setState 能够经过第二个参数 setState(partialState, callback),在回调方法中拿到更新后的结果。

十 React this 问题

返回目录

在 React 中有几种方法能够修正 this 的指向,这里例举 4 种方法:

import React, { Component } from 'react'

class App extends Component {
  constructor (props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick () {
    console.log('jsliang 2020');
  }
  handleClick2 = () => {
    console.log('jsliang 2021');
  }
  render () {
    // 四种绑定方法
    return (
      <div className='App'>
        {/* 方法一:经过 constructor 中进行 bind 绑定 */}
        <button onClick={this.handleClick}>btn 1</button>

        {/* 方法二:在里边绑定 this */}
        <button onClick={this.handleClick.bind(this)}>btn 2</button>

        {/* 方法三:经过箭头函数返回事件 */}
        <button onClick={() => this.handleClick()}>btn 3</button>
        
        {/* 方法四:让方法变成箭头函数 */}
        <button onClick={this.handleClick2}>btn 4</button>
        
        {/* 额外:直接调用不须要绑定 this */}
        {this.handleClick()}
      </div>
    )
  }
}

export default App;

那么,使用 bind 和箭头函数有什么区别吗?

箭头函数除了代码少,与普通函数最大的不一样就是:this 是由声明该函数时候定义的,通常是隐性定义为声明该函数时的做用域 this

经过 bind 的话,至关于:Foo.prototype.a = function() {},是经过原型链的一个指正绑定。

而经过箭头函数的话,就至关于:

class Foo {
  constructor() {
    this.a = () => {};
  }
}

十一 受控组件和非受控组件

返回目录

Ant Design 中,对 Input 输入框进行操做,若是是改变 defaultValue 会发现毫无做用。

这是由于 React 的 form 表单组件中的 defaultValue 一经传递值后,后续改变 defaultValue 都将不起做用,被忽略了。

具体来讲这是一种 React 非受控组件,其状态是在 input 的 React 内部控制,不受调用者控制。

因此受控组件就是能够被 React 状态控制的组件。双向数据绑定就是受控组件,你能够为 form 中某个输入框添加 value 属性,而后控制它的一个改变。而非受控组件就是没有添加 value 属性的组件,你并不能对它的固定值进行操做。

十二 组件通信

返回目录
  • 父组件向子组件通信:父组件向子组件传 props 方式,向子组件进行通信。
  • 子组件向父组件通信:父组件在 props 中传递方法,而后子组件调用这个方法,将自身须要传递的信息,传递到父组件的做用域中。
  • 复杂点的通信:借助 React 的 Context,或者 Redux 进行数据通信。

十三 Redux

返回目录

网上有挺多关于 ReduxReact-ReduxRedux-Saga 的使用,这里就不废话介绍了,仍是讲讲 jsliang 在工做中的一个使用吧。

工做目录
- 某个页面文件夹
   - View.jsx     当前页面主入口
   - Child.jsx    子组件
   - Brother.jsx  兄弟组件
   - action.js    动做
   - types.js     类型
   - saga.js      调用接口
   - reducers.js  处理数据

正常的一个工做目录如上所示,咱们工做中是怎么个使用方式呢?

首先,在 View.jsx 中经过 React-Redux 链接 ReactRedux

而后,假设如今 Child.jsx 须要调用接口(异步处理),那么会:

  1. action.js 中定义这个方法,会传递什么参数。
  2. 其中 types.js 是辅助 action.js 的一个内容,为了防止方法体的重复,咱们会在 types.js 中定义大写的 action 名字。
  3. 这时候就能够在 View.jsx 中经过 dispatch 触发方法,例如 dispatch(getPage(page, perPage))
  4. 这时候,在 reducers.js 中和 sage.js 中都能监听到这个方法,可是咱们是在 sage.js 中调用接口并处理数据。
  5. 处理完毕以后,再将 sage.js 中的传递给 reducers.js 中,让它去处理数据。

接着,若是 Brother.jsx 只是单纯地想处理数据并在 Child.jsx 中使用,那么咱们处理方式是跟上面同样的,只是直接在 reducers.js 中处理,而不须要再在 sage.js 中调用接口而已。

最后,咱们再看看 reduxreact-reduxt 的工做流程加深印象:

Redux

React-Redux

十四 Mixin、HOC 和 Hook

返回目录

前端发展速度很是之快,页面和组件变得愈来愈复杂,如何更好的实现 状态逻辑复用 一直都是应用程序中重要的一部分,这直接关系着应用程序的质量以及维护的难易程度。

MixinHOCHook 是 React 采用的 3 种 状态逻辑复用 的技术,Mixin 已被抛弃,HOC 正当壮年,Hook 初露锋芒,掌握它迭代因素和规律很是重要。

14.1 Mixin

返回目录

Mixin(混入)是一种经过扩展收集功能的方式,它本质上是将一个对象的属性拷贝到另外一个对象上面去。

不过你能够拷贝任意多个对象的任意个方法到一个新对象上去,这是继承所不能实现的。

它的出现主要就是为了解决代码复用问题。

可是,它会带来一些危害:

  • Mixin 相互依赖、相互耦合,不利于代码维护
  • 不一样的 Mixin 中的方法可能会互相冲突
  • Mixin 很是多时,组件是能够感知到的,甚至还要为其作相关处理,这样给代码形成滚雪球式的复杂性。

14.2 高阶组件(HOC)

返回目录

基于 Mixin 的问题,React 推出对装饰模式的一种实现:高阶组件(HOC)。

高阶组件接收一个组件做为参数,并返回一个新的组件。

高阶组件( HOC)是 React 中的高级技术,用来重用组件逻辑。但高阶组件自己并非 React API。它只是一种模式,这种模式是由 React 自身的组合性质必然产生的。
function visible(WrappedComponent) {
  return class extends Component {
    render() {
      const { visible, ...props } = this.props;
      if (visible === false) return null;
      return <WrappedComponent {...props} />;
    }
  }
}

高阶组件能够应用于 日志打点、可用权限控制、双向绑定、表单校验等。

高阶组件解决了 Mixin 带来的问题:

  • 低耦合。高阶组件就是一个没有反作用的纯函数,各个高阶组件不会互相依赖耦合
  • 避免冲突。高阶组件也有可能形成冲突,但咱们能够在遵照约定的状况下避免这些行为
  • 反作用小。高阶组件并不关心数据使用的方式和缘由,而被包裹的组件也不关心数据来自何处。高阶组件的增长不会为原组件增长负担

可是,有光的地方总有暗,高阶组件也存在一些缺陷:

  • HOC 须要在原组件上进行包裹或者嵌套,若是大量使用 HOC,将会产生很是多的嵌套,这让调试变得很是困难。
  • HOC 能够劫持 props,在不遵照约定的状况下也可能形成冲突。

14.3 Hook

返回目录

Hook 是 React v16.7.0-alpha 中加入的新特性。它可让你在 class 之外使用 state 和其余 React 特性。

使用 Hook,你能够在将含有 state 的逻辑从组件中抽象出来,这将可让这些逻辑容易被测试。

同时,Hook 能够帮助你在不重写组件结构的状况下复用这些逻辑。

因此,它也能够做为一种实现状态逻辑复用的方案。

Hook 使用带来的好处:

  • 减小状态逻辑复用的风险HookMixin 在用法上有必定的类似之处,可是 Mixin 引入的逻辑和状态是能够相互覆盖的,而多个 Hook 之间互不影响,这让咱们不须要在把一部分精力放在防止避免逻辑复用的冲突上。
  • 避免地狱式嵌套:大量使用 HOC 的状况下让咱们的代码变得嵌套层级很是深,使用 Hook,咱们能够实现扁平式的状态逻辑复用,而避免了大量的组件嵌套。
  • 让组件更容易理解:在使用 class 组件构建咱们的程序时,他们各自拥有本身的状态,业务逻辑的复杂使这些组件变得愈来愈庞大,各个生命周期中会调用愈来愈多的逻辑,愈来愈难以维护。使用 Hook,可让你更大限度的将公用逻辑抽离,将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割。
  • 使用函数代替 class:相比函数,编写一个 class 可能须要掌握更多的知识,须要注意的点也越多,好比 this 指向、绑定事件等等。另外,计算机理解一个函数比理解一个 class 更快。Hooks 让你能够在 class 以外使用更多 React 的新特性。

十五 性能优化

返回目录
  1. 首屏渲染优化<div id="root"> SVG </div>,也可使用插件 prerender-spa-plugin 插件进行首屏渲染。
  2. 页面切换优化。使用 html-webpack-plugin 插件自动插入 loading,这样切换的时候,就不须要在每一个页面都写一套 loading
  3. 减小业务代码体积。经过 Tree Shaking 来减小一些代码。
  4. 提取公共代码。经过 SplitChunkPlugin 自动拆分业务基础库,减小大文件的存在。
  5. 切分代码。经过 Code Splitting 来懒加载代码,提升用户的加载体验。例如经过 React Loadable 来将组件改写成支持动态 import 的形式。
  6. 懒加载。React 能够经过 react-lazyload 这种成熟组件来进行懒加载的支持。
  7. 页面占位。有时候加载页面的文本、图片的时候,会出现 “闪屏” 的状况,好比图片或者文字没有加载完毕,对应位置空白,而后加载完毕,会忽然撑开页面,致使闪屏。这时候使用第三方组件 react-placeholder 能够解决这种状况。

十六 参看文献

返回目录

本系列有 67 篇参考文献。

16.1 面试知识点

返回目录

2019

2018:

2017

16.2 系统

返回目录

2017

16.3 React 和 Vue 比对

返回目录

2020

2017

16.4 生命周期

返回目录

最新

2019

2018

2017

16.5 受控组件和非受控组件

返回目录

2019

2016

16.6 Diff 和 虚拟 DOM

返回目录

2019

2018

2017

2015

16.7 React 源码

返回目录

2018

2017

2015

16.8 React Mixin

返回目录

2015

16.9 React Hoc

返回目录

2019

2017

16.10 React Hooks

返回目录

2019

2018

16.11 React Fiber

返回目录

2018

16.12 服务端渲染(SSR)

返回目录

16.13 性能优化

返回目录

2019

2018

2017

16.14 其余

返回目录

2019

2018

2017

2015


jsliang 的文档库由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的做品创做。<br/>本许可协议受权以外的使用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处得到。
相关文章
相关标签/搜索