在 react 当中,主要创建更新的有三种方式
1、ReactDOM.render || hydrate 
这两个api都是要把这个应用第一次渲染到我们页面上面,展现出来整个应用的样子的过程,这就是初始渲染
2、setState
3、forceUpdate
ReactDOM.render 需要做哪些事情呢?
1、创建 ReactRoot
2、创建 FiberRoot 和 RootFiber
3、创建更新
import React, { Component } from 'react'
import './App.css' class List extends Component {
state = {
a: 1,
b: 2,
c: 3,
} handleClick = () => {
this.setState(oldState => {
const { a, b, c } = oldState
return {
a: a * a,
b: b * b,
c: c * c,
}
})
} render() {
const { a, b, c } = this.state
return [
<span key="a">{a}</span>,
<span key="b">{b}</span>,
<span key="c">{c}</span>,
<button key="button" onClick={this.handleClick}>
click me
</button>,
]
}
} class Input extends Component {
state = {
name: 'jokcy',
} handleChange = e => {
// 这里如果使用方法设置`state`
// 那么需要现在外面读取`e.target.value`
// 因为在React走完整个事件之后会重置event对象
// 以复用event对象,如果等到方法被调用的时候再读取`e.target.value`
// 那时`e.target`是`null`
this.setState({
name: e.target.value,
})
} render() {
return (
<input
type="text"
style={{ color: 'red' }}
onChange={this.handleChange}
value={this.state.name}
/>
)
}
} class App extends Component {
render() {
return (
<div className="main">
<Input />
<List />
</div>
)
}
} export default App

这个 demo 非常简单,一个 Input  一个 List ,Input 里面有个 state ,属性有 name,然后 render 里面有个 input 标签,这个标签,输入的时候去改变这个 name 。

第二个组件是 List ,这个 List 里面有三个 span ,三个 span 里面分别显示 state 的属性 a,b,c ,然后点击 click ,a,b,c 以乘以自己的方式改变值。这就是这个 demo 的样子。 然后我们看一下最终要渲染这个应用,会调用 ReactDOM.render ,然后传入这个 App ,第二个是这个应用会挂载到的节点上
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'; ReactDOM.render(<App />, document.getElementById('root'))

我们这里传入的是 <App /> ,实际上我们传入的是 React.createElement。我们传进去的是 App 这个类,但是并没有创建这个实例,这个时候还什么东西都没有,因为我们只得到了一个 createElement,最终要形成一个页面渲染出来的过程,这就是 ReactDOM.render 要做的事情,然后看下这个源码,打开 ReactDOM.js。 react-dom 下面有不同的包,有 client , 有 server . 还有 shared , shared 是在 cilent 和 server 里面都会用到的。对应的就是渲染的平台不一样,server 就是在 Nodejs 下面进行渲染的一个工具包,我们主要了解的是 client 下面的东西。在 ReactDOM.js 下面,我们先找到定义 ReactDOM 的地方

const ReactDOM: Object = {
createPortal, findDOMNode(
componentOrElement: Element | ?React$Component<any, any>,
): null | Element | Text {
if (__DEV__) {
let owner = (ReactCurrentOwner.current: any);
if (owner !== null && owner.stateNode !== null) {
const warnedAboutRefsInRender =
owner.stateNode._warnedAboutRefsInRender;
warningWithoutStack(
warnedAboutRefsInRender,
'%s is accessing findDOMNode inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentName(owner.type) || 'A component',
);
owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrElement == null) {
return null;
}
if ((componentOrElement: any).nodeType === ELEMENT_NODE) {
return (componentOrElement: any);
}
if (__DEV__) {
return DOMRenderer.findHostInstanceWithWarning(
componentOrElement,
'findDOMNode',
);
}
return DOMRenderer.findHostInstance(componentOrElement);
}, hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
// TODO: throw or warn if we couldn't hydrate?
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
true,
callback,
);
}, render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}, unstable_renderSubtreeIntoContainer(
parentComponent: React$Component<any, any>,
element: React$Element<any>,
containerNode: DOMContainer,
callback: ?Function,
) {
invariant(
parentComponent != null && ReactInstanceMap.has(parentComponent),
'parentComponent must be a valid React Component',
);
return legacyRenderSubtreeIntoContainer(
parentComponent,
element,
containerNode,
false,
callback,
);
}, unmountComponentAtNode(container: DOMContainer) {
invariant(
isValidContainer(container),
'unmountComponentAtNode(...): Target container is not a DOM element.',
); if (container._reactRootContainer) {
if (__DEV__) {
const rootEl = getReactRootElementInContainer(container);
const renderedByDifferentReact =
rootEl && !ReactDOMComponentTree.getInstanceFromNode(rootEl);
warningWithoutStack(
!renderedByDifferentReact,
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by another copy of React.',
);
} // Unmount should not be batched.
DOMRenderer.unbatchedUpdates(() => {
legacyRenderSubtreeIntoContainer(null, null, container, false, () => {
container._reactRootContainer = null;
});
});
// If you call unmountComponentAtNode twice in quick succession, you'll
// get `true` twice. That's probably fine?
return true;
} else {
if (__DEV__) {
const rootEl = getReactRootElementInContainer(container);
const hasNonRootReactChild = !!(
rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl)
); // Check if the container itself is a React root node.
const isContainerReactRoot =
container.nodeType === ELEMENT_NODE &&
isValidContainer(container.parentNode) &&
!!container.parentNode._reactRootContainer; warningWithoutStack(
!hasNonRootReactChild,
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by React and is not a top-level container. %s',
isContainerReactRoot
? 'You may have accidentally passed in a React root node instead ' +
'of its container.'
: 'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.',
);
} return false;
}
}, // Temporary alias since we already shipped React 16 RC with it.
// TODO: remove in React 17.
unstable_createPortal(...args) {
if (!didWarnAboutUnstableCreatePortal) {
didWarnAboutUnstableCreatePortal = true;
lowPriorityWarning(
false,
'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +
'and will be removed in React 17+. Update your code to use ' +
'ReactDOM.createPortal() instead. It has the exact same API, ' +
'but without the "unstable_" prefix.',
);
}
return createPortal(...args);
}, unstable_batchedUpdates: DOMRenderer.batchedUpdates, unstable_interactiveUpdates: DOMRenderer.interactiveUpdates, flushSync: DOMRenderer.flushSync, unstable_flushControlled: DOMRenderer.flushControlled, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// Keep in sync with ReactDOMUnstableNativeDependencies.js
// and ReactTestUtils.js. This is an array for better minification.
Events: [
ReactDOMComponentTree.getInstanceFromNode,
ReactDOMComponentTree.getNodeFromInstance,
ReactDOMComponentTree.getFiberCurrentPropsFromNode,
EventPluginHub.injection.injectEventPluginsByName,
EventPluginRegistry.eventNameDispatchConfigs,
EventPropagators.accumulateTwoPhaseDispatches,
EventPropagators.accumulateDirectDispatches,
ReactControlledComponent.enqueueStateRestore,
ReactControlledComponent.restoreStateIfNeeded,
ReactDOMEventListener.dispatchEvent,
EventPluginHub.runEventsInBatch,
],
},
};

这段就是定义 ReactDOM 的地方。ReactDOM 是个对象,这个对象里面有 render 方法。这个 render 方法我们可以看到他接收的有三个参数。一个是 element ,就是 react element . 第二个是 container ,就是我们要挂载到哪一个节点上面。第三个是 callback ,callback 就是说这个应用渲染结束之后,他会调用这个 callback 。

然后里面直接调用了一个方法叫 legacyRenderSubtreeIntoContainer,然后传入了 null ,element,container,false,callback 这几个参数。
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
forceHydrate: boolean,
callback: ?Function,
) {
// TODO: Ensure all entry points contain this check
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
); if (__DEV__) {
topLevelUpdateWarnings(container);
} // TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
let root: Root = (container._reactRootContainer: any);
if (!root) {
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
originalCallback.call(instance);
};
}
// Initial mount should not be batched.
DOMRenderer.unbatchedUpdates(() => {
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
});
} else {
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
originalCallback.call(instance);
};
}
// Update
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
}
return DOMRenderer.getPublicRootInstance(root._internalRoot);
}

这个就是 legacyRenderSubtreeIntoContainer 这个方法,第一个参数 null ,他对应的是 parentComponent 这个参数

let root: Root = (container._reactRootContainer: any);

然后下面他获取了 root 这么一个变量,他通过 container , container 就是 DOMContainer ,就是 ReactDOM.render 里面的第二个参数,就是挂载的节点,它上面有没有 _reactRootContainer 这个属性,第一次渲染的时候,这个属性值肯定是没有的,接下来没有的话,他就会去创建这个属性值,通过调用 legacyCreateRootFromDOMContainer 这个方法

function legacyCreateRootFromDOMContainer(
container: DOMContainer,
forceHydrate: boolean,
): Root {
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// First clear any existing content.
if (!shouldHydrate) {
let warned = false;
let rootSibling;
while ((rootSibling = container.lastChild)) {
if (__DEV__) {
if (
!warned &&
rootSibling.nodeType === ELEMENT_NODE &&
(rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)
) {
warned = true;
warningWithoutStack(
false,
'render(): Target node has markup rendered by React, but there ' +
'are unrelated nodes as well. This is most commonly caused by ' +
'white-space inserted around server-rendered markup.',
);
}
}
container.removeChild(rootSibling);
}
}
if (__DEV__) {
if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
warnedAboutHydrateAPI = true;
lowPriorityWarning(
false,
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
'will stop working in React v17. Replace the ReactDOM.render() call ' +
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
);
}
}
// Legacy roots are not async by default.
const isConcurrent = false;
return new ReactRoot(container, isConcurrent, shouldHydrate);
}

我们看到这个方法,他接收两个参数,第一个是 DOMContainer , 第二个是 forceHydrate ,这个 forceHydrate 就是传入的第四个参数,也就是 render 方法里面的第四个参数,是 false , render 方法里面是写死的,是不是代表着永远都是 false 呢,这个主要对应的是另外一种情况,就是 hydrate 这个方法,hydrate 跟 render 本质是一样的,唯一的一个区别就是是否会调和 container 里面的 html 节点,是否要复用这些节点。主要是有服务端渲染的时候会使用 hydrate 这个 api。因为服务端渲染 和 客户端渲染 第一次得到的节点是一样的。这个时候如果可以复用这些节点,可以提高一定的性能。所以他两唯一的区别就是第四个参数是 true 还是 false 。

我们再回到 legacyCreateRootFromDOMContainer ,对于 render 来说,这个 forceHydrate 是 false。那么如果是 false 的情况,会去调用这个方法 shouldHydrateDueToLegacyHeuristic。
function shouldHydrateDueToLegacyHeuristic(container) {
const rootElement = getReactRootElementInContainer(container);
return !!(
rootElement &&
rootElement.nodeType === ELEMENT_NODE &&
rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
);
}

这个方法里面 return 的是 rootElement 。rootElement 是通过调用 getReactRootElementInContainer 得到的

function getReactRootElementInContainer(container: any) {
if (!container) {
return null;
} if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}

这个方法先判断一下这个 container 是否存在,如果不存在, return,如果存在,再继续判断这个节点是否等于 DOCUMENT_NODE ,就是 document ,就是 window.document ,是否是等于的。如果是等于,就直接返回这个 document 。 如果不是等于,就返回这个 container 的子节点。再回到 shouldHydrateDueToLegacyHeuristic ,判断完他是否是个节点,而且是 ROOT_ATTRIBUTE_NAME,然后返回,这个不是特点重要,跟 react 整体更新没有多大关系,再回到 legacyCreateRootFromDOMContainer 。当这个节点是 false 的情况下,执行了一个 for 循环,这个 for 循环是干嘛的呢?就是把 container 传进来的所有子节点都删掉,因为我们认为 shouldHydrate 为 false 的情况下,在渲染出来后,这些子节点已经不能用了,因为不需要合并他。最后return 一个 new ReactRoot。他传进去的是 container, isConcurrent, shouldHydrate 。 isConcurrent 我们看到直接是 false,已经注明了 root 节点不应该是 async 的。接下来我们看一下 new 一个 root 的过程

function ReactRoot(
container: Container,
isConcurrent: boolean,
hydrate: boolean,
) {
const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);
this._internalRoot = root;
}
ReactRoot.prototype.render = function(
children: ReactNodeList,
callback: ?() => mixed,
): Work {
const root = this._internalRoot;
const work = new ReactWork();
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'render');
}
if (callback !== null) {
work.then(callback);
}
DOMRenderer.updateContainer(children, root, null, work._onCommit);
return work;
};
ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work {
const root = this._internalRoot;
const work = new ReactWork();
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'render');
}
if (callback !== null) {
work.then(callback);
}
DOMRenderer.updateContainer(null, root, null, work._onCommit);
return work;
};
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
callback: ?() => mixed,
): Work {
const root = this._internalRoot;
const work = new ReactWork();
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'render');
}
if (callback !== null) {
work.then(callback);
}
DOMRenderer.updateContainer(children, root, parentComponent, work._onCommit);
return work;
};
ReactRoot.prototype.createBatch = function(): Batch {
const batch = new ReactBatch(this);
const expirationTime = batch._expirationTime; const internalRoot = this._internalRoot;
const firstBatch = internalRoot.firstBatch;
if (firstBatch === null) {
internalRoot.firstBatch = batch;
batch._next = null;
} else {
// Insert sorted by expiration time then insertion order
let insertAfter = null;
let insertBefore = firstBatch;
while (
insertBefore !== null &&
insertBefore._expirationTime <= expirationTime
) {
insertAfter = insertBefore;
insertBefore = insertBefore._next;
}
batch._next = insertBefore;
if (insertAfter !== null) {
insertAfter._next = batch;
}
} return batch;
};

ReactRoot 方法里面通过 DOMRenderer.createContainer(container, isConcurrent, hydrate) 创建了 root 节点。那么 DOMRenderer 是什么东西呢?

import * as DOMRenderer from 'react-reconciler/inline.dom';

DOMRenderer 其实就是 react-reconciler 下面的 inline.dom 。 inline.dom 里面只引用了 ReactFiberReconciler.js 。打开这个js 之后,找到 createContainer。

export function createContainer(
containerInfo: Container,
isConcurrent: boolean,
hydrate: boolean,
): OpaqueRoot {
return createFiberRoot(containerInfo, isConcurrent, hydrate);
}

发现这个方法里面创建了一个 createFiberRoot 。返回到 ReactRoot ,创建完之赋值给 this._internalRoot。然后再赋值给了 container._reactRootContainer ,最后,调用到了 root.render,就是原型链上的 render 方法。这里创建了一个 reactWork,这个不是特别重要,就跳过了。最终调用了 DOMRenderer.updateContainer。

export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
const current = container.current;
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, current);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
callback,
);
}

updateContainer 他接收了 element,container,parentComponent。这个方法里面有三个变量。一个 current, 一个 currentTime。一个 expirationTime 。 expirationTime 是通过computeExpirationForFiber 获得的, expirationTime是非常重要的。然后调用 updateContainerAtExpirationTime 。

export function updateContainerAtExpirationTime(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// TODO: If this is a nested container, this won't be the root.
const current = container.current; if (__DEV__) {
if (ReactFiberInstrumentation.debugTool) {
if (current.alternate === null) {
ReactFiberInstrumentation.debugTool.onMountContainer(container);
} else if (element === null) {
ReactFiberInstrumentation.debugTool.onUnmountContainer(container);
} else {
ReactFiberInstrumentation.debugTool.onUpdateContainer(container);
}
}
} const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
} return scheduleRootUpdate(current, element, expirationTime, callback);
}
这里的 context 是没有的,最后调用了 scheduleRootUpdate。
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
if (__DEV__) {
if (
ReactCurrentFiber.phase === 'render' &&
ReactCurrentFiber.current !== null &&
!didWarnAboutNestedUpdates
) {
didWarnAboutNestedUpdates = true;
warningWithoutStack(
false,
'Render methods should be a pure function of props and state; ' +
'triggering nested component updates from render is not allowed. ' +
'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
'Check the render method of %s.',
getComponentName(ReactCurrentFiber.current.type) || 'Unknown',
);
}
} const update = createUpdate(expirationTime);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element}; callback = callback === undefined ? null : callback;
if (callback !== null) {
warningWithoutStack(
typeof callback === 'function',
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
update.callback = callback;
}
enqueueUpdate(current, update); scheduleWork(current, expirationTime);
return expirationTime;
}

这里面创建了 createUpdate。update 是用来标记,react 中需要标记更新的地点的,这个方法就是设置了一些update 相关的属性。最后调用 enqueueUpdate 。后面调用了 scheduleWork ,调度,为什么有调度呢,因为有任务优先级的概念。

React源码 ReactDOM.render的更多相关文章

  1. react源码之render

    1.最近学习react源码,刚刚入门,看了render的原理,到了fiberRoot的创建 如图:

  2. React源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理.本系列文章希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期(C ...

  3. React 源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理. 本系列文章 希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期 ...

  4. React躬行记(16)——React源码分析

    React可大致分为三部分:Core.Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版 ...

  5. React源码之组件的实现与首次渲染

    react: v15.0.0 本文讲 组件如何编译 以及 ReactDOM.render 的渲染过程. babel 的编译 babel 将 React JSX 编译成 JavaScript. 在 ba ...

  6. react 源码之setState

    今天看了react源码,仅以记录. 1:monorepo (react 的代码管理方式) 与multirepo 相对. monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个mo ...

  7. 读react源码准备

    git源码地址:https://github.com/facebook/react react 里面就是 react源码 react里面的react文件夹就是react源码,react源码非常的少,总 ...

  8. React源码 commit阶段详解

    转: React源码 commit阶段详解 点击进入React源码调试仓库. 当render阶段完成后,意味着在内存中构建的workInProgress树所有更新工作已经完成,这包括树中fiber节点 ...

  9. React源码解析:ReactElement

    ReactElement算是React源码中比较简单的部分了,直接看源码: var ReactElement = function(type, key, ref, self, source, owne ...

随机推荐

  1. ES5.1下的作用域链

    作用域链(ES5.1规格视角) javascript作用域类型 javascript采用的是词法作用域(静态作用域) 与之相对的是动态作用域,以下的例子可以很好地说明 let a = 3 functi ...

  2. 项目:git+gitlab+jenkins+ansible上线网站

    项目需求 1. 在gitlab中创建一个项目 nginxinstall2. 编写playbook,实现一键部署nginx.部署一个静态测试页.测试部署结果要求: 部署nginx 端口:83 运行身份: ...

  3. [LeetCode] 647. Palindromic Substrings 回文子字符串

    Given a string, your task is to count how many palindromic substrings in this string. The substrings ...

  4. [LeetCode] 633. Sum of Square Numbers 平方数之和

    Given a non-negative integer c, your task is to decide whether there're two integers a and b such th ...

  5. 微信小程序登录那些事

    最近团队在开发一款小程序,都是新手,一边看文档,一边开发.在开发中会遇到各种问题,今天把小程序登录这块的流程整理下,做个记录. 小程序的登录跟平时自己APP这种登录验证还不太一样,多了一个角色,那就是 ...

  6. Chrome操作技巧

    好用的插件: 如果你用 Chrome 浏览器,这8款插件一定要用! - 知乎 沙拉查词:     集合各大翻译,详细好用权威 Simple Allow Copy: 开启被网页屏蔽的自由复制功能 Qui ...

  7. Elasticsearch由浅入深(四)ES并发冲突、悲观锁与乐观锁、_version乐观锁并发

    ES并发冲突 举个例子,比如是电商场景下,假设说,我们有个程序,工作的流程是这样子的: 读取商品信息(包含了商品库存) 用户下单购买 更新商品信息(主要是将库存减1) 我们比如咱们的程序就是多线程的, ...

  8. SQLServer之Case用法

    定义 计算条件列表,并返回多个可能的结果表达式之一. 表达式类型 case具有两种格式: 简单case表达式,它通过将表达式与一组简单的表达式进行比较来确定结果. case搜索表达式,它通过计算一组布 ...

  9. webapi ClaimsPrincipal使用

    参考文档:ClaimsPrincipal Class 个人demo:SwaggerDemoApi 今天看到一段代码懵逼了 var principal = new ClaimsPrincipal(new ...

  10. mvc_第二遍

    之前,我们学习了mvc最基本的架构和用法. 在学习了真正的属性.委托和列表之后,我们就可以使用微软提倡的模型了. 需要注意的是,这种模型以及它的工作方式,身上有着浓浓的微软印记.在方便的同时,也要自行 ...