该文入选IVWEB社区,感谢垂青眼。点我惊喜
后来看到朋友一篇前端思维转变–从事件驱动到数据驱动 | 被删的前端游乐场,从事件驱动和数据驱动的角度来理解,对本文是一个很好的补充,感谢@珊姐
缘起
笔者刚入行的时候,做的是Android客户端开发。虽然从事的时间不长,但稍微了解一点基本的概念。后来阴差阳错从事Web开发,一直到现在,所以现在多多少少对Web领域有一丁点的见解。因为这样,所以有时候会思考下二者的共性,想找一下二者相同的点。最近有两个问题,一直萦绕着:
React
中有state
,props
的概念。组件component
的显示,数据的体现 大部分都是由state承载,props传递。而android基本都是通过setXX
去控制组件和数据。为什么会有这样的差异?- 同时,
redux
等状态管理组件都是flux
架构的实现,也有很多开发者提出FluxAndroid
的概念,但google官方并不承认flux的架构。这是为什么?
Android开发中的常见写法
我们经常看到这样的写法,首先在xml
文件中定义我们的布局文件,指定id
等属性。
<TextView android:text="@string/hello_world"
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
同时,在Activity
中通过findViewById
去获取控件的引用,然后进行一系列操作,比如setText
等
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.textview);
TextView textView = new TextView(this);
textView.setText("Hey, one more TextView");
}
除此之外,我们还经常看见这样的写法,比如:
setVisibility(View.VISIBLE);
imageview.setBackgroundColor(Color.rgb(213, 0, 0));
你有没有发现,这些写法有一个共性,就是都是通过setXXX
去设定值。也就是说,我们开发者在操作的时候,有这样的一个模式:
- 获取到值(不管是从数据库还是网络拉取)
- 这个值经过处理,得到可以目标控件需要的值
- 通过
setXXX
去设定该值 - 然后界面发生变化
后来我发现,这样的模式在JQuery中也是类似的。
JQuery中的常见写法
先看下这段代码。
<!DOCTYPE html>
<html lang="en">
<head>
<title>learn jQuery</title>
</head>
<body>
<h1>例子1</h1>
<p>如果你点我,我就会消失。</p>
<p>继续点我!</p>
<p>接着点我!</p>
<script src="jquery-3.3.1.js"></script>
<script type="text/javascript">
//例子1
$(document).ready(function () {
console.log("document is ready");
$("p").click(function () {
$(this).hide();
});
});
</script>
</body>
</html>
首先,我们先写了四行HTML
代码。
然后,通过JQuery
去操作,注意我们的操作方式。
我们通过$("p")
去获取document
对象中的<p></p>
元素,这是不是很像刚刚在上一节提到的findViewById
,有木有?
获取到元素后,通过$(this).hide()
对该元素进行操作。这像不像textView.setText("Hey, one more TextView");
如果觉得这段代码不直观,来看这个。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Evan JQery</title>
<script src="jquery-3.3.1.js"></script>
<script>
$(document).ready(function(){
$("#btn1").click(function(){
$("#test1").text("Hello world!");
});
$("#btn2").click(function(){
$("#test2").html("<b>Hello world!</b>");
});
$("#btn3").click(function(){
$("#test3").val("Dolly Duck");
});
});
</script>
</head>
<body>
<p id="test1">这是段落。</p>
<p id="test2">这是另一个段落。</p>
<p>Input field: <input type="text" id="test3" value="Mickey Mouse"></p>
<button id="btn1">设置文本</button>
<button id="btn2">设置 HTML</button>
<button id="btn3">设置值</button>
</body>
</html>
同时,你可以再本地打开上面的代码,在浏览器中预览,然后打开开发者模式,在控制台中输入,
$("#test3").val("Dolly Ducrrrrk");
你会发现网页内容也随之发生了改变。
命令式编程
维基百科这样说:
Imperative programming is a programming paradigm that uses statements that change a program’s state.
简单理解,命令式编程,就是命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
上述了两小节都是命令式编程。
声明式编程
提到命令式编程,不得不说下声明式编程。
声明式编程,就是告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
打个比方:
Declarative Programming is like asking your friend to draw a landscape. You don’t care how they draw it, that’s up to them.
Imperative Programming is like your friend listening to Bob Ross tell them how to paint a landscape. While good ole Bob Ross isn’t exactly commanding, he is giving them step by step directions to get the desired result.
声明式就像你告诉你朋友画一幅画,你不用去管他怎么画的细节
命令式就像按照你的命令,你朋友一步步把画画出来
如果你觉得有点分不清楚二者区别,别着急,先看下下面的案例。
React中的常见写法
(完成源码将放在文末可下载)
'use strict';
const e = React.createElement;
class LikeButton extends React.Component {
//https://www.cnblogs.com/johnzhu/p/9016277.html
constructor(props) {
super(props);
this.state = { liked: false };
}
render() {
if (this.state.liked) {
return 'You liked this.';
}
return e(
'button',
{ onClick: () => this.setState({ liked: true }) },
'Like'
);
}
}
const domContainer = document.querySelector('#like_button_container');
ReactDOM.render(e(LikeButton), domContainer);
React
是声明式的,以上就是体现声明式很好的例子。
在我们的render
方法中,渲染一个按钮,按钮名字为“Like”。
按钮的状态(也就是文字内容)受state
控制,初始化的state
为一个JavaScript对象。
this.state = { liked: false };
点击按钮之后,触发事件,改变state
{ onClick: () => this.setState({ liked: true }) }
好,state
在点击前后发生改变,按钮的文字是由state
控制。
换句话说,我们并没有直接操作DOM
去改变按钮的文字内容,只是通过改变state
的状态。
而state
不同的值描述了按钮在不同情况下应该如何表现不同的内容。
The differences here may be subtle.
The React example never actually touches an element. it simply declares an element should be rendered given our current state. It does not actually manipulate the DOM itself.
和命令式的区别有点模糊。React
没有直接操作元素,它只是声明了元素如何在给定的状态下渲染出特定的结果。
所以,React
的思想和JQuery
有很大不同。
When writing React, it’s often good not to think of how you want to accomplish a result, but instead what the component should look like in it’s new state.
使用React
的时候,最好不同去想着如何实现某个结果,而是组件在新的状态下应该有什么样的表现。
歇一口气 ^ __ ^ 歇一口气
其实,React 中也有命令式,具体可以参考
Refs and the DOM
Vue的常见写法
看了React
的写法,知道它是声明式,那我们来看看Vue
是如何体现响应式的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VueTest -- evan</title>
</head>
<body>
<div id="app">
<h2>{{product}} are in stock</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const app = new Vue({
el: '#app', /*element的缩写*/
data: {
product: 'Boots'
}
})
</script>
</body>
</html>
我们在模板中指定
<h2>{{product}} are in stock</h2>
然后再Vue
实例中只要product
有值,立马就渲染出来。
为了更好的演示,请你打开上述代码,在浏览器中预览,然后打开控制台。输入
app.product = 'you input here'
你就会发现屏幕上的网页渲染立马发生了改变。
这就是响应式。
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。
来我们简单解释下 Vue 响应式系统的底层细节。
第一,还记得我们通过下面的代码,把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项。
const app = new Vue({
el: '#app', /*element的缩写*/
data: {
product: 'Boots'
}
})
Vue 将遍历此对象所有的属性。
第二,Vue使用 Object.defineProperty
把这些属性全部转为 getter/setter。
第三,每个Vue Component的render
函数,都都对应一个 watcher 实例。
第四,当 Vue Component render
函数被执行的时候, data 上会被 触碰(touch)
, 即被读, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)。
第五,data 被改动时(主要是用户操作), 即被写, setter
方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新。
你肯定熟悉响应式
还记当时风靡一时的RxJava
, RxAndroid
嘛?如今又有了RxKotlin
,RxDart
。(提醒和我一样,做过Android开发的同学们,哈哈~)
他们都是ReactiveX家族的一员。
试着回答
React
中有state
,props
的概念。组件component
的显示,数据的体现 大部分都是由state承载,props传递。而android基本都是通过setXX
去控制组件和数据。为什么会有这样的差异?
个人愚见,Android客户端和Web开发中,有这么几点不同:
-
Android的界面UI是有
XML
标签定义,和DOM
有些不同。 -
浏览器刷新的时候,整个
DOM
结构都会更新,而Android没有刷新整个页面的概念,在Android中,你是通过measure
(测量)、layout
(定位)、draw
(绘制)去显示一个View
。 -
不同于React中的
Component
,Android中的View
,比如ImageView
、TextView
本身内置了很多方法来控制自身属性,比如setBackground
、setText
。 -
(欢迎大佬斧正、补充)
redux
等状态管理组件都是flux
架构的实现,也有很多开发者提出FluxAndroid
的概念,但google官方并不承认flux的架构。这是为什么?
先简单说明下,Android的架构。
Android架构合集
这篇文章中列举了很多Android架构,包括官方google的,同时也有Flux
架构模式。
很多开发者受Flux的启发,写了适用Android的Flux架构模式。
比如下面几篇文章。
AndroidFlux ——当ANDROID遇到了FLUX,架构ANDROID应用的新方式。
Flux Architecture on Android
Flux Android Architecture Components using Kotlin
这个问题我还没有很好的答案。我自己以Flux的架构模式去做过了Android的实践,由于Android Activity等自身的组件,使得用Flux架构反而比较繁琐,有点过度设计的味道。但究竟为何Google官方不推荐,欢迎大家补充~
彩蛋
这就是 Dan 所说的,React 并不是完全的响应式设计的意思。React 需要你手动跟踪应用数据,并在数据变化时告诉 React,这也意味着你得做更多工作。
难道这就是Vue这个响应式框架近几年越来越流行的原因?
放出彩蛋,就是svelte
哦对了,文中所有的源码在这里哈
点我下载