nervjs / nerv Goto Github PK
View Code? Open in Web Editor NEWA blazing fast React alternative, compatible with IE8 and React 16.
Home Page: https://nerv.aotu.io
License: MIT License
A blazing fast React alternative, compatible with IE8 and React 16.
Home Page: https://nerv.aotu.io
License: MIT License
CN document Link failed
I perf Nerv with the other frameworks using it on top of Cordova and i really like what I see.
I also use Cerebral for my state management, which took me sometime to get it to work. After a while I got it to work with this hack.
https://github.com/andrewvmail/momoboiler/blob/master/src/entry.js#L58
The cerebral/react binding code is here
https://github.com/cerebral/cerebral/blob/next/packages/node_modules/%40cerebral/react/src/Hoc.js#L76
Nerv reactcreate element output signature is slightly different than the react counterpart. props is inside the vnode.props so i use the hack to make that work.
just wondering if the Nerv team have any thoughts on this, perhaps a less hacky way of getting this to work?
Thanks!
Hi there,
I've just started playing around with Nerv and I wanted to implement Jest as the testing framework but I'm running into some issues: The webpack dev server running into a compilation error.
My package.json looks like this:
"scripts": {
"dev": "webpack-dev-server --config webpack.config.js --mode development --port 8888",
"test": "jest",
"test:watch": "jest --watch"
},
"dependencies": {
"nervjs": "^1.2.15"
},
"devDependencies": {
"@babel/core": "^7.0.0-beta.42",
"@babel/plugin-transform-react-jsx": "^7.0.0-beta.42",
"@babel/preset-env": "^7.0.0-beta.42",
"babel-core": "^6.26.0",
"babel-jest": "^22.4.3",
"babel-loader": "8.0.0-beta.0",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"html-webpack-plugin": "^3.0.7",
"jest": "^22.4.3",
"nerv-devtools": "^1.2.15",
"react-test-renderer": "^16.2.0",
"regenerator-runtime": "^0.11.1",
"webpack": "^4.2.0",
"webpack-cli": "^2.0.12",
"webpack-dev-server": "^3.1.1"
}
My babelrc is:
{
"presets": [
[
"env",
{
"spec": true,
"useBuiltIns": false
}
]
],
"plugins": [
[
"transform-react-jsx",
{
"pragma": "Nerv.createElement"
}
]
]
}
And my webpack.config.js is:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './index.html'
})
]
}
After I finished the getting started guide on Jest, by installing a couple of dependencies, yarn add --dev babel-jest babel-core regenerator-runtime
, the webpack dev server started to error: ERROR in ./src/App.js Module build failed: TypeError: Cannot read property 'bindings' of null
When we launch a React web app with react bootstrap, it doesn't works because react-bootstrap can't get the react version.
Console return this error "TypeError: Cannot read property 'split' of undefined" and debug tool show error in utils.js:50 of uncontrallable dependency of react-bootstrap.
Thanks
Hey, do you have official MobX bindings?
ie8上报default关键字不支持
各位nerv开发大佬你们好。
我看到有提供nerv-server包,提供了字符串模板输出(包括Prefetching这些?)。
但如果用next.js也没问题吧。
路由是沿用社区react-router,还是说后期你们会出一个官方nerv-router?
目前nerv应用在了jd.com,我看源码是服务端渲染+客户端交互渲染?
需要写两份代码吗,server/clinet各一份?
还是说nerv ssr方案自带组件反解?
希望大佬能百忙之中抽空解答,不胜感激。
Thanks for the React alternative. Does it support React 16's Fragments syntax?:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
因为我参照了很多的资料,但总是无法配置出一个能够在 IE8 下使用的 DEMO。
I am using 1.2.4-beta.1
When I am using react-inform
and nerv-server - it crashes the render.
state.values
seems to be undefined
, so values[name]
is crashing in this file: https://github.com/theadam/react-inform/blob/master/src/form.js
However when I render this in browser - everything is working as expected.
I'm not using anything fancy with react-inform
so you should be able to reproduce this with basic example, but if not - I may try to isolate the issue out of my project and make a repo out of it.
第一时间 star 了 nerv, 然后近两天有空, 打算试用一下.
则每个Component.jsx需要引入 Nerv, 否则编译时会报 Nerv is not defined. 比如以下纯文字组件:
import Nerv from 'nervjs' //必须, 因为 return 后的语句实际上转成了 Nerv.createElement(xxx)
export default function About() {
return <div>me, <span className="red" style={{'color':'red'}}>red</span></div>
}
按正常逻辑, 其实上面这个组件完全用不着 import 什么
install后, require('es5-polifill')
报找不到, 查看一下 es5-polyfill 包, 发现其根目录下没有任何JS文件(而是放在了dist下),导致其无法被 require
这就让readme里提到的兼容IE8的路断了(当然, 改一下 es5-polyfill包可以解决)
配置后编译会报 Error: Plugin/Preset files are not allowed to export objects, only functions.
初步估计是 babel 8.0 beta版与preset-es3的兼容问题 (话说我也不知道为什么快速开始里要使用 babel beta版)
出现奇怪问题: 点击链接路由跳转 (hash变了),但页面并没有加载出对应路由的内容; 此时按f5刷个新, 能正常载入对应路由
将组件完全复制到 react 项目, 只是替换 Nerv 为 react, 则页面完全正常
以上问题最严重的是 react-router
问题, 没法继续开发了 💥
希望更新下文档, 在IE8 兼容方面详细准确一些
nerv-devtools always works well ,but when add the react-router-dom , it is error !
TypeError: Cannot read property 'type' of null
createReactDOMComponent
E:/demos/ReactApp/nerv-app/node_modules/nerv-devtools/dist/index.esm.js:74
71 | _currentElement: isText
72 | ? vnode.text
73 | : {
74 | type: vnode.type,
75 | props: normalizeProps(vnode.props)
76 | },
77 | _renderedChildren: normalizeChildren(vnode.children),
I created repository where you can reproduce the issue.
https://github.com/Havunen/bug-react-bootstrap-nerv/tree/master
NervJS should be able to render 3 different types of modals when button is clicked. The above repository contains steps to reproduce the issue.
I found a weird bug and I'm not sure what the cause is.
I have the following code:
<div id="app">
<Header location={this.state.location} />
<main className="d-flex justify-content-center">
<HashRouter onLocationChanged={this.handleLocationChanged}>
<Route key="liked" hash="#/">
<Liked />
</Route>
<Route key="saved" hash="#/saved">
<Saved />
</Route>
<Route key="about" hash="#/about">
<About />
</Route>
</HashRouter>
</main>
</div>
HashRouter
render function:
return this.state.render
In the state, only one child is saved (double checked with console.log and dev tools). However, when I render a different component (so to say, a route change is happening), this happens:
So it seems Nerv is adding the component/a Node to the DOM instead of replacing it. Both <Liked />
and <About />
render a <div>
, the latter one with className="..."
.
Code is here: https://github.com/kurtextrem/Improved-for-Instagram/blob/master/src/components/HashRouter.js#L152 / https://github.com/kurtextrem/Improved-for-Instagram/blob/master/src/components/app.js#L44
Some of the packages have git URLs that point to old repos. e.g. nerv-redux
specifies repository https://github.com/NervJS/nerv-redux but the actual module is being pushed from the monorepo.
Babel directly uses the github URLs in packages: @babel/core
uses https://github.com/babel/babel/tree/master/packages/babel-core, @babel/cli
uses https://github.com/babel/babel/tree/master/packages/babel-cli, etc
I want to replace React to nerv i clojurescript react binding regent, but with errors
{:source-paths ["src"]
:dependencies [[reagent "0.8.0-alpha2"]]
:builds {:app {:target :browser
:js-options
{:resolve
{"react" {:target :npm
:require "nervjs"}}
{"react-dom" {:target :npm
:require "nervjs"}}
{"create-react-class" {:target :npm
:require "nerv-create-class"}}}
:output-dir "public/js"
:asset-path "js"
:modules {:main {:entries [starter.core]}}
:devtools {:before-load starter.core/stop
:after-load starter.core/start
:http-root "public"
:http-port 8020}}}}
if need more info, please comment
先贴下代码:
nerv/packages/nerv/src/event.ts
Lines 189 to 203 in 8d74124
不太明白为什么要做这个样的处理,这个明显会导致form在ie8下面失效,想不明白,
望解答
表现为 点链接 hashchange 了, 但页面没变化; 但此时F5刷新,能渲染指定路由
另开issue放代码. 代码是常见的 react 结构: index 是entry, 它引入 app 并渲染; app引入了router和两个组件 hello和about, 组合后导出
Hello.jsx
import Nerv from 'nervjs'
class Hello extends Nerv.Component {
constructor() {
super(...arguments);
this.state = {
message: 'world'
}
}
render() {
return (
<div>
Hello, {this.state.message}
</div>
)
}
}
export default Hello
About.jsx
import Nerv from 'nervjs'
export default function About() {
return <div>me, <span className="red" style={{'color':'red'}}>red</span></div>
}
App.jsx
import Nerv from 'nervjs'
import { HashRouter as Router, Route, Link } from 'react-router-dom';
import Hello from './Hello';
import About from './About';
// import Topics from './Topics';
const App = () => (
<Router>
<div>
<div className="header">
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
</nav>
</div>
<hr />
<Route exact path="/" component={Hello} />
<Route path="/about" component={About} />
{/* <Route path="/topics" component={Topics} /> */}
</div>
</Router>
);
export default App
index.jsx
// require('es5-polyfill')
import Nerv from 'nervjs'
import App from './App'
Nerv.render(<App />, document.getElementById('app'))
如有需要, 我再提供 webpack.config.js , 也是初级代码. 对了, 我用的 webpack最新版:
"webpack": "^4.4.1",
"webpack-cli": "^2.0.13",
"webpack-dev-server": "^3.1.1"
The readme and website make a bunch of extraordinary claims ("same api, much faster, better browser compat, smaller bundle size"), but what are the tradeoffs of choosing Nerv vs vanilla React? What do I miss when I choose Nerv?
Note: Not trying to be a pain in the butt here, genuinely curious why React wouldn't just take this code base and make it their own
快速开始文档过于简单,最好像vue官网那样包装好,包括webpack的常用配置等,再配上一个demo;
Reproduction: https://github.com/JounQin/1stg/tree/nervjs
Branch: nervjs
Alias: https://github.com/JounQin/1stg/blob/nervjs/build/base.js#L79-L80
Steps: yarn dev
and visit http://localhost:4000
const AppContainer = __DEV__ ? require('react-hot-loader').AppContainer : ({ children }) => children
Actually both types of AppContainer
throw errors.
NervJS:
https://codesandbox.io/s/m31owy1wvj (link fixed)
React: ( See console logs )
https://jsfiddle.net/a2ay2fh6/
expected result: Nerv should be able to change root nodes.
For more tests on similar manner see Inferno test cases here:
https://github.com/infernojs/inferno/blob/master/packages/inferno-create-element/__tests__/components.spec.jsx#L2528-L3219
Hopefully you find good way to fix this :) So we can improve inferno too. Inferno currently updates vNode parents after setState to have this working + cloning vNodes before patching.. Which has obvious effect in performance
Typically there is a CHANGELOG.md
in the root or GitHub Releases page is utilized to explain changes.
For example, what changed in version 1.2.3?
For comparison, see React which puts changes in both places:
Although nervjs explicitly stated that createFactory
is not supported currently, it is quite an important top level API which is required by recompose which is further required by many hoc libraries
I tried to install nerv-test-utils
to replace react-dom/test-utils
but it seems that the package does not contain all of the source files. The index.js
tried to require ./src/index.js
but the entire src
folder is excluded from packaging in .npmignore
.
the link for radium https://github.com/FormidableLabs/radium.
i will show a sanbox later
When visiting:
with:
FF 57.0.3 (macOS High Sierra)
Chrome 63.0.3239.84 (macOS High Sierra)
Marker texts such as 'size_desc' where one would expect actual text to be present.
No errors in console.
If I click on the paging dot of the examples carousel I get:
ReferenceError: event is not defined
index.js:1:775917
t/e.switchCode https://nerv.aotu.io/js/index.js:1:775917
onClick https://nerv.aotu.io/js/index.js:1:780157
e https://nerv.aotu.io/js/index.js:1:40029
i https://nerv.aotu.io/js/index.js:1:39903
nerv支持ie8,使用的react16,与之配套的react-router4 ,不支持ie8
如果使用react-router3,由于使用的是react16,又不与之兼容
这样就开发不了多页面应用啦~
有支持ie8与之配套的前端路由推荐吗
请问框架提供了SSO能力、或者oath2协议,如何保持用户的session?
Hi,
In Nerv's website there are a few benchmarks comparing its performance to that of React & a few other frameworks. Looks like those are all benchmarks for client-side rendering.
What is Nerv's performance in server-side rendering? Especially when compared to React & Next.js? Any benchmarks available?
Thanks.
React flags components with .isReactComponent
.
https://github.com/facebook/react/search?utf8=%E2%9C%93&q=isreactcomponent&type=
Next.js (and maybe others) rely on that:
https://github.com/zeit/next.js/blob/a32b22bb2d07c2959d65e11448098988fb1a721a/server/render.js#L102
Thus nerv can't be used with next.js right now:
vercel/next.js#3868
Using Material ui components does not work with nervjs
.
Steps to reproduce:
create-react-app
nervjs
material-ui
packageimport React, { Component } from 'nervjs'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import ActionHome from 'material-ui/svg-icons/action/home'
const App = () => (
<MuiThemeProvider>
<ActionHome />
</MuiThemeProvider>
)
The error will be like:
Error: Unsupported VNode.
▶ 15 stack frames were collapsed.
./src/index.js
src/index.js:8
5 | import registerServiceWorker from './registerServiceWorker'
6 | import './index.css'
7 |
> 8 | render(<App />, document.getElementById('root'))
9 | registerServiceWorker()
10 |
11 |
View compiled
▶ 6 stack frames were collapsed.
I tried to debug this but got nowhere...
Messing with my code I got it to render but the test in jest fails and it does not render the paths for the SvgIcon.
It renders:
<svg __source="[object Object]" __self="[object Object]" viewBox="0 0 24 24" style="display: inline-block; color: rgba(0, 0, 0, 0.87); fill: currentcolor; height: 24px; width: 24px; user-select: none; transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;"></svg>
Instead of:
<svg viewBox="0 0 24 24" style="display: inline-block; color: rgba(0, 0, 0, 0.87); fill: currentcolor; height: 24px; width: 24px; user-select: none; transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></svg>
as react does.
Click two buttons
Expected: console log should only has "updated"
Results: Console log gets "mounted" and "unmounted".
Basically, if parent component B has unkeyed children, and there are children add/remove before child component A, even though the changed child is not a component A, A still gets mounted/unmounted, because unkeyed children are diffed in sequential order.
In the Inferno way, it has normalized VNode so if key is not set, it might use children order and null
is considered a child too, so child component always has the same normalized key, thus gets the correct diffing results.
I see that there is documentation on various webpack/babel configuration but the building process is missing from documentation. Wouldn't it be better if there's a couple of paragraphs added to the docs to go over the build process?
I have been trying to build a react development environment with zero NPM and bundler dependency.
So far I was able to use nerv
source code with minimal changes. It only needs TypeScript to transpile tsx files to js. The goal is to use browser's ES2015 modules support, at least for development time.
This repo works in Chrome 63:
https://github.com/malekpour/nopack
I copied nerv source files in the src/external
folder and the only thing I had to change was adding .js
extension to import statements.
// import { render } from './render'
import { render } from './render.js'
Ideally we should be able to reference ES2015 libraries from CDN's just like what we used to do for jQuery or Angular 1.
unmount callbacks should be called before removing DOM.
See https://jsfiddle.net/Luotcj1f/ for React behavior.
at least this line in wrong:
https://github.com/NervJS/nerv/blob/master/packages/nerv/src/vdom/patch.ts#L95-L96
Didn't check any other places.
So I have a small preact application and I wanted to replace preact with nerv
. After aliasing react
and react-dom
I replaced render
import from preact-render-to-string
to nerv-server
and started bundled application (I am bundling server code as well, so my file loaders works as expected)... and the server crashed on this:
const global = (function () {
// the only reliable means to get the global object is
// `Function('return this')()`
// However, this causes CSP violations in Chrome apps.
if (typeof self !== 'undefined') {
return self;
}
if (typeof window !== 'undefined') {
return window;
}
if (typeof global !== 'undefined') {
return global;
}
throw new Error('unable to locate global object');
})();
with
if (typeof global !== 'undefined') {
^
ReferenceError: global is not defined
The code is from installed nerv-server/dist/index.js
I was surprised at first (crashing on typeof
), but well, look at this:
const a = (() => {
console.log(typeof a)
})();
If you paste this into browser console it will always fail, because you're trying to get value of a
while evaluating its value.
I have no idea how this can work anywhere and get to the "unable to locate ..." error, however I saw an closed Issue that was just about unable to locale
being thrown.
Do you have any example repos with server-side rendering, so I can look closer how to do that properly. As my typical React/preact configuration seems not to work here.
In other words... why is everyone wasting their time reinventing the wheel so what people just want to be famous? Is this another China "Me too" moment?
//.babelrc配置
"plugins": [
[
"transform-react-jsx",
{
"pragma": "Nerv.createElement"
}
],
[
"module-resolver",
{
"root": ["."],
"alias": {
"react": "nervjs",
"react-dom": "nervjs",
"create-react-class": "nerv-create-class"
}
}]
]
// page/index.js
import React from 'react'
export default () => <div>Welcome to next.js!</div>
As mentioned above,nerv is very good. how about adding a beautiful UI library?
现在 Nerv 和 preact 一样,compat, ssr, redux, test-utils 等分开在不同的仓库。这些天的开发目前有几个痛点:
isWidget()
,导致每个仓库都要维护一份这个方法。同时打包出来也每个包都有一个;Lerna 就能很好地解决这些问题,除上述问题之外还提供 change log 自动生成的功能和统一管理 semver,这样多人维护和解决历史 bug 就可以少翻 commit history 和 git blame 了。
现在三大框架 react, angular, vue 和一些大型前端项目例如 babel 也都在用,至少看起来也专业不少。另外这样也能 star 聚拢起来而不是分散到各个 repo,万一我们火了呢 :)
版本 "nervjs": "^1.2.17",
按照文档这里操作
使用 Webpack
只需要在 alias 把 nervjs 和 react、react-dom 关联起来即可:
{
resolve: {
alias: {
'react': 'nervjs',
'react-dom': 'nervjs'
}
}
}
The hydrate
function seems to be removing all children indiscriminately.
The ReactDOM version of hydrate works by hydrating the existing markup, attempts to attach event listeners to the existing markup and fixes mismatched text content where appropriate.
Not just switch, the question starts when creating a new project based on Typescript.
If the original project use libraries like react-router
, it will depends on .d.ts
file from React
. In javascript world this won't be a problem cause there's no strong typed definition force you to follow the right type, but Typescript will not work.
Is there any way to get around this in practice? To gain the benefit both from strong typed language(with better Intellisense and much more) and performance from Nerv
.
Thought you guys may have encountered this problem when actually working on Nerv
because the source codes are written in Typescript.
*
* A: -> [a b c d] <-
* B: -> [a b d] <-
*
这里我们可以发现前置元素 a, b 和 后置元素 d 都是相同的。所以我们可以将这样的 diff 情况转变为:
*
* A: -> [c] <-
* B: -> [] <-
*
这里我们可以发现 A 和 B 的不同就是多了一个 c 元素,所以只要进行把 c 元素移除这一步可以完成 diff。相反地,如果这步操作 A 为空,B 有多余元素,那么我们就将多余元素插入即可。
这一步 diff 优化最早由 Neil Fraser 提出,实现非常简单(在 Neil 的文章里他提供了 JavaScript,Java,Python 三种实现),优化的力度也非常大,但真正首次应用到前端框架却是十年后的 Bobril,后来号称性能最强的 Inferno 也使用了这一优化。
再来看下一种情况:
*
* A: -> [a b c d e f g] <-
* B: -> [a c b h f e g] <-
*
在这个情况的 diff 中,我们可以应用之前的优化策略把他简化为:
*
* A: -> [b c d e f] <-
* B: -> [c b h f e] <-
*
首先我们创造一个数组 P
,它代表新数组应该被插入的位置。
*
* A: [b c d e f]
* B: [c b h f e]
* P: [. . . . .] // . == -1
*
接下来我们再创建一个索引 I
,作为新数组位置的映射(如果新旧数组都很小的话,那我们直接遍历数组即可,反之我们需要一个索引):
*
* A: [b c d e f]
* B: [c b h f e]
* P: [. . . . .] // . == -1
* I: {
* c: 0,
* b: 1,
* h: 2,
* f: 3,
* e: 4,
* }
* last = 0
*
有了数组 P
和 索引 I
之后,现在我们可以开始遍历旧数组
*
* A: [b c d e f]
* ^
* B: [c b h f e]
* P: [. 0 . . .] // . == -1
* I: {
* c: 0,
* b: 1, <-
* h: 2,
* f: 3,
* e: 4,
* }
* last = 1
*
第一步遍历我们可以发现旧数组的 b 元素也在新数组上,所以我们把他在旧数组的位置 0 放到他在数组 P 相对应的位置上。
这里我们同时还要维护一个变量 last,它代表访问过的节点在新集合中最右的位置(即最大的位置)。如果新集合中当前访问的节点比 last 大,说明当前访问节点在旧集合中就比上一个节点位置靠后,则该节点不会影响其他节点的位置,因此不必执行移动操作。只有当访问的节点比 last 小时,才需要进行移动操作。
*
* A: [b c d e f]
* ^
* B: [c b h f e]
* P: [1 0 . . .] // . == -1
* I: {
* c: 0, <-
* b: 1,
* h: 2,
* f: 3,
* e: 4,
* }
* last = 1 // last > 0; moved = true
*
这里我们访问元素 c 的位置为 0,而 last 为 1,所以我们需要进行移动操作。
这一步实际上也是 React Stack Reconciler(React 16 以下)的核心算法:每次遍历的时候把当前节点在旧集合的位置和 last 进行对比,如果 mountIndex 大于 last,那么就把他添加到差异队列,然后更新 last,全部遍历完毕之后再遍历差异队列,一步一步进行 patch。
(这里如果理解有障碍的话建议把常规的排序算法都撸一遍,可以重点看选择排序和插入排序)
*
* A: [b c d e f]
* ^
* B: [c b h f e]
* P: [1 0 . . .] // . == -1
* I: {
* c: 0,
* b: 1,
* h: 2,
* f: 3,
* e: 4,
* }
* moved = true
*
这里元素 d 在新数组当中并不存在,所以它应该被移除掉。
*
* A: [b c d e f]
* ^
* B: [c b h f e]
* P: [1 0 . . 3] // . == -1
* I: {
* c: 0,
* b: 1,
* h: 2,
* f: 3,
* e: 4, <-
* }
* moved = true
这里把元素 e 在旧数组的位置(3)放到数组 P
上。
*
* A: [b c d e f]
* ^
* B: [c b h f e]
* P: [1 0 . 4 3] // . == -1
* I: {
* c: 0,
* b: 1,
* h: 2,
* f: 3, <-
* e: 4,
* }
* moved = true
*
这里把元素 f 在旧数组的位置(4)放到数组 P
上。
到了这一步,如果是 React 的算法已经走完了。但不妨思考以下的 diff 情况:
*
* A: [a b c d]
* B: [d a b c]
*
这里肉眼一看就知道,把旧集合的 d 元素移动到 0 这一步操作就可以完成 diff。但根据前文所述的逻辑走一次,实际上 React 会先把 a, b, c 移动到他们的相应的位置 + 1,一共三步操作。
移动操作也并不是没有副作用,仍然可能会重启动画,focus 丢失,重置滚动条位置等。所以我们还可以进行下一步优化:
这里我们需要找到 P
数组的[最长递增子序列(longest increasing subsequence)] 来做动态规划,新集合中不属于这个序列的将会被移动。
还是用我们之前的例子:
*
* A: [b c d e f]
* B: [c b h f e]
* P: [1 0 . 4 3] // . == -1
* LIS: [1 4]
* moved = true
*
这里我们同时尾部遍历新数组和 LIS 序列,查看元素的位置是否能与 LIS 序列的任何一个值匹配。
*
* A: [b c d e f]
* B: [c b h f e]
* ^ // new_pos == 4
* P: [1 0 . 4 3] // . == -1
* LIS: [1 4]
* ^ // new_pos == 4
* moved = true
*
这一步能匹配,e 可以呆在它原来的位置上。
*
* A: [b c d e f]
* B: [c b h f e]
* ^ // new_pos == 3
* P: [1 0 . 4 3] // . == -1
* LIS: [1 4]
* ^ // new_pos != 1
* moved = true
*
f 的位置不能匹配,把他移动到 e 之前。
*
* A: [b c d e f]
* B: [c b h f e]
* ^ // new_pos == 2
* P: [1 0 . 4 3] // . == -1
* ^ // old_pos == -1
* LIS: [1 4]
* ^
* moved = true
*
h 的位置为 -1,把他插入。
*
* A: [b c d e f]
* B: [c b h f e]
* ^ // new_pos == 1
* P: [1 0 . 4 3] // . == -1
* LIS: [1 4]
* ^ // new_pos == 1
* moved = true
*
b 能匹配 LIS,可以老老实实呆着。
*
* A: [b c d e f]
* B: [c b h f e]
* ^ // new_pos == 0
* P: [1 0 . 4 3] // . == -1
* LIS: [1 4]
* ^ // new_pos != undefined
* moved = true
*
c 不能匹配,把它移动到 b 之前。
走完这一步我们的 diff 算法就完毕了。
这时回头再来看看之前把尾部元素插入到头部的情况,React 需要经过 3 次操作,而经过我们动态规划之后:
*
- A: [a b c d]
- B: [d a b c]
- P: [3 0 1 2]
- LIS: [0 1 2]
*
可以发现 a, b, c 能匹配 LIS,可以呆到天荒地老,我们只要把 d 移动到头部的位置就可以完成 diff。Perfect!
详细代码:
https://github.com/NervJS/nerv/blob/patch/packages/nerv/src/vdom/patch.ts
require('nervjs')
throws in Node.js:
Error: unable to locate global object
at /Users/rauchg/Projects/next-news/node_modules/nervjs/dist/index.js:17:11
at Object.<anonymous> (/Users/rauchg/Projects/next-news/node_modules/nervjs/dist/index.js:18:2)
at Module._compile (module.js:660:30)
at Object.Module._extensions..js (module.js:671:10)
at Module.load (module.js:573:32)
at tryModuleLoad (module.js:513:12)
at Function.Module._load (module.js:505:3)
at Module.require (module.js:604:17)
at require (internal/module.js:11:18)
I had to work around it by manually setting self = this
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.