0%

背景

最近接手公司的一个移动端项目,是通过 Rax 作为 dsl 开发的,在发布的时候构建多分代码,在 APP 端编译为能够运行在 weex 上的代码,在 H5(跑在浏览器或者 webview 里面,不管什么技术我们统称 H5) 端使用降级的 weex

这一套开发体系,看起来很完美,一次开发,三端运行。但是真实在开发的时候,就不是这么完美了。由于毕竟是跑在 weex 上的,而不是浏览器。所以在开发方式上也很难直接从 web 端的开发方式平移过去,为了实现跨端运行,所以在样式上只实现了 Css 的子集, DOM API 也是如此,开发的时候,在浏览器里面调试的时候,一切正常,但是等发布到 APP 端用 weex 跑的时候又是各种问题,开发体验很不流畅。

当然,有人会说那是因为你对 weex 的 api 不了解,也对,一直以来对与这种自己搞一套非标准体系来实现各种魔法功能的东西都不怎么感兴趣 但是如果了解它的成本大于了它带来的收益,那么对我们来说,就没有必要做这件事情。

weex 相对于 H5 ,最大的优点在于交互性能上要更好一点。

而随着手机性能的提升,以及 webview 的不断优化,H5 的页面也越来越流畅了,尤其是纯展示形页面上。而且相较于 H5 ,weex 天生不具备 seo 能力,同时存在分享传播困难的缺点,这样看来,使用 weex 的理由就更少了。而且我们在一个新业务上使用 H5 开发了一个页面,借助同构以及提前缓存的能力,已经把首屏打开速度做到了全球秒开,而且业务数据也达到预期,所以我们打算把现有的存量业务都迁移到 H5 上。

这就是我为什么要把基于 Rax 开发的模块代码转换为 React 代码,也就有了本篇文章。
本文针对的 rax 版本是 0.6.8 ,1.x 的版本改动很大,不再本文讨论范围内。

期望的目标

对于一个 rax 模块,我们期望通过编译后:

  1. 能够在 react 下运行
  2. 尽可能提取样式到 css 文件里,不使用内联

不同之处

Rax 在开发之处,就是为了能够使用 React 的语法来开发 weex ,所以一开始在语法上和 React 几乎一致。后续随着 rax 的不断迭代,渐渐和 react 有了一些不一样的地方,但是差距不大。
我们对比一下同一个模块在 rax 和 react 的实现代码:

rax module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { Component, createElement, findDOMNode } from "rax";
import Text from "rax-text";
import View from "rax-view";
import styles from "./index.css";

class Kisnows extends Component {
constructor(props) {
super(props);
this.state = {
count: 1,
};
}

handleClick = () => {
this.setState({
count: this.state.count + 1,
});
};

render() {
const { count } = this.state;
const { name } = this.props;
return (
<View style={styles.wrap}>
<Text style={[styles.name, { color: "red" }]}>{name}</Text>
<View onClick={this.handleClick}>
怕什么真理无穷,进一步有进一步的好。
</View>
<View>点击进步:{count}</View>
</View>
);
}
}

export default Kisnows;

react module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { Component } from "react";
import { findDOMNode } from "react-dom";
import styles from "./index.css";

class Kisnows extends Component {
constructor(props) {
super(props);
this.state = {
count: 1,
};
}

handleClick = () => {
this.setState({
count: this.state.count + 1,
});
};

render() {
const { count } = this.state;
const { name } = this.props;
return (
<div className="wrap">
<h1 className="name" style={{ color: red }}>
{name}
</h1>
<div onClick={this.handleClick}>
怕什么真理无穷,进一步有进一步的好。
</div>
<div>点击进步:{count}</div>
</div>
);
}
}

export default Kisnows;

可以看到他们的区别如下:

阅读全文 »

三年前写了一篇网易一年工作工作总结,如今换工作到了阿里,到今天也算满一年了,总结一下这一年的工作。

本想如法炮制,再写一篇总结,谁知从 6.4 号刚好满一周年到今天,已经拖了一个月了。

虽然已经拖了一个多月了,但真的没空写。

估计这篇文章后面也不会有时间写了,就这样吧。

过了今天,就在这个世界上满满的混了 25 年了,如果要活 100 岁的话,现在过了 1/4 ,算是一个里程碑吧。

回首过去

过去 25 年,整体上还算比较顺利。

侥幸

高一班级组织春游,大家去爬山,爬到山顶休息聊天,我看到山下有两个同学。不知道是脑抽了还是怎么了,就非常想下去找他们聊天,于是就往山下走。由于走的不是常规下山路线,所以不是台阶式的,都是野路。就那么走,谁知重力作用导致我越走越快,最后跑了起来。是的,要在外人看来应该就是一个傻逼从山上往山下跑,我就记得我眼前的景象越来越模糊,速度妥妥地超过博尔特了。结果,腿实在换不过来,绊了一下,结果就以前空翻的姿势朝山下滚去,那几条人体抛物线应该是画的挺完美的。当时我的大脑一片空白,想着可能我这伟大的一生竟然就要交代到这里了,叹息,不过想想这毕竟也是埋皇帝的地方,值了。还没来得及想其他的,突然被一块超大的石头挡住了,就那么把我拦在了半山腰了。停下来后,感觉头上好像在流汗,就下意识去擦,擦完一看,我靠,竟然红色的,完了,完了,这回得躺医院了。
结果呢,最后去医院检查,只是皮外伤加轻微胃出血,掉了两天消炎针就活蹦乱跳的去学校了。

真是幸运,如果没有那块大石头可能就真的交代到我大 x 陵的半山腰了。

求学

后来高中就愉快的打了三年游戏,导致高中三年实验班、重点班、普通班,通通呆了一遍。印象深刻的是,高三的一节化学课上,老师说这个高二已经学过了,我们就不讲了,我:一脸懵逼.jpg。回家赶紧去翻化学课本,发现还真有,就花了一个月的时间把高二课程好好补了下,然后就又愉快的去玩毒奶粉了。以至于高中三年玩网游的经历,直接让我看透了网游的本质,毕业到现在也再没碰过。还有一件有意思的事情,有天晚上我通宵上网,第二天上早读直接趴桌子睡了。我们班主任,一个数学老师,人称“老汉”。过来看我在睡觉,拧着耳朵就把我从课桌上拎了起来,然后让大家安静,“大家不要学他,老是晚上开夜车学习,不知道这样睡太晚会影像第二天上课的吗?” 全班哄堂大笑,都知道我去通宵打游戏了

后来高考,虽然语文没考好,但英语超长发挥,最终以高一本线一分的成绩,勉强上了大学。

大学吧,选了石油地质专业,想着以后就要向李四光一样为祖国找油了,怀揣着这么一种悲壮的英雄主义情怀进了大学,然后又打了四年游戏。到大三下学期找工作的时候,四级考了四次没过,还连着考了两次 424 。以至于中石油来校开双选会,面试人家前面都聊的好好的,一看四级没过,直接刷了。我当时非常沮丧,然后回宿舍继续愉快的打我的 Dota 。还有件有趣的事情,斯伦贝谢来我校招聘,我去面试。人家一面纯英文面,我这渣渣英语赶紧好好准备了一下,到我的时候我就开始背我准备的自我介绍加专业知识,结果没背两句就忘了后面的,于是就在那胡诌,皱了半天我都不知道我说的是啥。完事,人面试官给我来一句,“you are so lovely” , 然后就这么过了一面,神奇。因为四级没过,当时找工作非常不顺利,然而我还一点都不慌,后来没复习又考了一次四级,竟然过了。

成绩单还没下来的时候,中海油来校招,拿着简历 + 网上打印的成绩单去面试,终于面过收到了 offer ,不容易。

工作

哪里有石油,那里就是我的家

大学毕业,先去中海油总部天津培训了一个月,然后被分到了深圳分公司,去开发南海。哇,南海啊,祖国的蓝色国土啊,就要靠我了。在深圳搞了好久的安全培训,然后再一次迎新酒会上,被告知我分到了 HYSY981 平台。我的天,我很是太幸运了,981 可是当时国内最先进的半潜式平台,当时别提有多高兴了。天天盼着出海,在盼星星盼月亮等出海的过程中,本着怕掉海里淹死的恐惧强行自学了游泳。
大概到 9 月底的时候告诉我们可以出海了,然后一帮同事从深圳搞到了湛江分公司,在那里领导请我们大吃了一顿,从下午五点吃到凌晨两点,说是为我们送行。第二天又坐船从湛江到海南,等直升机又等了将近两周。直升机来的前一天,一帮大佬又请我们大吃了一顿。说是出海前吃点好的,从海上回来要再大吃一顿,这是一种习俗,毕竟有出去回不来的危险。

阅读全文 »

由于合规要求,所以文章一些名词或者数据进行了脱敏处理。

背景

加入阿里做的第一个项目,由于是很老的业务,前后端不分离,大部分逻辑都在后端 velocity 模版里面。

作为一个前端,我们开发时在没有后端环境的支持情况下,想要调试 vm 模版,这真的是一件极其痛苦的事情。

因为对于任何一个需求的,都需要:

  1. 在后端工程里面修改 vm 代码
  2. 在 aone 上部署后端工程,这一步需要大概半个小时的时间
  3. 部署成功后,刷新浏览器进行验证

也就是说,任何一行代码的修改成本都至少要半个小时。而且开发时严重依赖后端同学的支持,比如我要前端显示分支流程中 A 流程,那么需要后端同学配合吐出 A 流程的数据,然后等半个小时,我要前端显示分支流程中的 B 流程,又得半个小时。如果后端同学有事情支持不了,我前端开发得暂时搁置一下了。

做了一个需求后,我发现这整个开发流程真的太痛苦了,而且效率也相当低下。一个我觉得两天就能搞定的需求,搞了整整一周,还通宵了一天才勉强赶上进度。

而且觉得阿里的技术不如想象中的那么先进,但这也是个机会,于是就想有没有什么好的解决方案。

方案

阅读全文 »

泰国

落地签

我是飞到曼谷素万那普机场,下机后跟着人流直走,头上有指示牌,可以一路走到办理落地签的地方。
办理落地签有两个通道,一个普通通道,一个快速通道。快速通道 2200🐖,普通通道 2000🐖。
建议走快速通道,能省很多时间。

阅读全文 »

工作

年初,公司经历大的变动,整个理财业务线都干掉了,一大批人转岗的转岗、离职的离职。前端技术部这里,人也走了几个,六楼本来 5 个前端,最后只剩下了 3 个。原来的项目也都不做了,开始做的新的项目网易白条。风风火火做了将近一年,现在产品的数字还比较好看,年后就会上公开申请制对外正式开放,前景还是不错的。

只是,现在楼上两个前端年后又要走一个人,流动性那么大,没有一点归属感。加上阿里很多部门给我打电话让我过去面试,想想网易不怎么高的工资,有时候都有点动心,但还是一一拒绝了。起码在网易待满三年,好好沉淀一下。也磨练一下自己的性子,否则天天换工作,也不利于长久的职业发展。只是最近新上的区块链项目,虽然我没参与,但能让我感觉整个部门非常的浮躁,又在犹豫呆下去是否是正确的。

生活

今年整体上还是过的像一碗白开水,中间偶有放点糖,但很快也就没有味道。

身体上已经明显能感觉到开始横向发展了,体重上了五六公斤,但是身体素质却下降了,引体向上只能做一个,我的天呐,大学的时候好歹也能轻松做十个的,这衰减的也太快了。说要锻炼身体,然而基本上只停留在嘴上,锻炼一次中间能间隔一个月,哈哈。

有时候觉得无聊,就想给自己培养个爱好,想来想去最后决定学个口琴,投入小,见效快。然后就买琴网上找教程,刚开始的几个月,还比较上心,基本天天练习,到现在不复杂的曲子都可以吹,而且自我感觉还不错。就是因为学的是布鲁斯口琴,低音的 6 需要压音,到现在也不能很好的压出好听的声音,更别提融入到曲子中去了,还需要继续练习啊。现在最喜欢的吹的曲子有两个,一个是 《alone》 一个是 《you are my sunshine》,心情不好的时候就自己吹给自己听。

日本旅游

11 月左右,公司组织我们出国旅游,有三个地方可选:日本、塞班岛、澳洲。最后我选了日本,也是第一次出国,还是很开心。

去了北海道,日本的大农村,第一天入住的小镇下午五点大街上就一个人都没有了,跟同事出逛了一圈最后发现酒店附近的居酒屋七点以后开门。一群人进去想去感受日本当地的居酒屋文化,只是老板就一个人,而且跟我们语言不通,完全照顾不过来,看起来有点不是很开心的样子,不过我们喝的倒是很开心。

第二天去的第一个景点就是个动物园,这也是我第一次去动物园,看到了企鹅、狼、海豹、河马、长颈鹿,就是出来的时候差点掉队找不到组织。

后来又去了北海道厅、洞爷湖、熊厂、地狱谷、当天晚上入住的洞爷湖酒店环境真好。

中间还去了哪都忘了,最后落脚在札幌市区,然后第二天自由活动。白天逛街买东西,几个大商场同事们都买了好多东西,都是土豪啊,哈哈。

期间有件很意思的事情,我想买个 SONY 1000x ,结果和售货员就这个耳机的输入电压是多少沟通了好久,用各种翻译软件都不行,说英语不行、说汉语也不行。

阅读全文 »

介绍

引用 MDN 的介绍:

IndexedDB 是一个事务型数据库系统,类似于基于 SQL 的 RDBMS。 然而不同的是它使用固定列表,IndexedDB 是一个基于 JavaScript 的面向对象的数据库。 IndexedDB 允许您存储和检索用键索引的对象; 可以存储 structured clone algorithm 支持的任何对象。 您只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列事务中的数据。

IndexedDB  是一种低级 API,用于客户端存储大量结构化数据(包括, 文件/ blobs)。该 API 使用索引来实现对该数据的高性能搜索。虽然  Web Storage  对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太有用。IndexedDB 提供了一个解决方案。

区别

这是我整理的 WebStorage 和 indexedDB 的之间区别,有问题的地方还请指出。

用法

1. 打开数据库

阅读全文 »

当我们在开发 React 项目中,第一次调用 ReactDOM.render 的时候都发生了什么呢?
今天就从源码角度来追踪一下这个问题(主要看流程, 而不纠结与细节)。

ReactDOMStackEntry

首先我们可以从 ReactDOM 的入口文件 ReactDOMStackEntry.js 中找到 render 方法。可以看到,render 方法是 ReactMount 组件提供的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var ReactDOMStack = {
findDOMNode: findDOMNode,
render: ReactMount.render,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
version: ReactVersion,

/* eslint-disable camelcase */
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
unstable_renderSubtreeIntoContainer: ReactMount.renderSubtreeIntoContainer,
/* eslint-enable camelcase */

__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// For TapEventPlugin which is popular in open source
EventPluginHub: require("EventPluginHub"),
// Used by test-utils
EventPluginRegistry: require("EventPluginRegistry"),
EventPropagators: require("EventPropagators"),
ReactControlledComponent: require("ReactControlledComponent"),
ReactDOMComponentTree,
ReactDOMEventListener: require("ReactDOMEventListener"),
ReactUpdates: ReactUpdates,
},
};

ReactMount.render

1
2
3
4
5
6
7
8
render: function(nextElement, container, callback) {
return ReactMount._renderSubtreeIntoContainer(
null,
nextElement,
container,
callback,
);
},

又调到了 _renderSubtreeIntoContainer 方法, 这个方法核心内容如下:

阅读全文 »

这个模块的代码非常简单,短短十几行。

1
2
3
4
5
6
7
8
9
10
11
12
var ReactElement = require("ReactElement");

var invariant = require("fbjs/lib/invariant");
function onlyChild(children) {
invariant(
ReactElement.isValidElement(children),
"React.Children.only expected to receive a single React element child."
);
return children;
}

module.exports = onlyChild;

就是判断传入的 children 是不是一个合法的 React 元素,否则就抛错出来。判断的逻辑就在 ReactElement 模块里面:

1
2
3
4
5
6
7
ReactElement.isValidElement = function (object) {
return (
typeof object === "object" &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
};
阅读全文 »

ReactElement

这个模块定义了 React 元素的行为和方法,首先看 ReactElement 函数:

ReactElement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
// This tag allow us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,

// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,

// Record the component responsible for creating this element.
_owner: owner,
};

if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};

// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, "validated", {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self and source are DEV only properties.
Object.defineProperty(element, "_self", {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, "_source", {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}

return element;
};

抛去其中 DEV 里面的内容,其他部分很简单,定义了一个 React 元素应有属性。包括: type, key, ref, self, source, owner, props.

阅读全文 »