代码来源:https://codepen.io/dagger8224/pen/myeKgVQ
任务:实现“按钮点击 +1”计数器,对比不同框架的写法与特性,并给出选型建议。
目录
- 代码解读(逐框架)
- jQuery(命令式 DOM)
- AngularJS(作用域 + 指令)
- Vue(响应式 + 模板指令)
- React(状态 + 渲染函数)
- dagger.js(原生 DOM 上的声明式指令,零样板)
- 方案对照(要点一览)
- 技术观察与解读
- 什么时候选谁?
- 附:关键代码片段
代码解读(逐框架)
jQuery(命令式 DOM)
HTML 一个按钮 #jquery
,JS 里手动挂事件并改 innerHTML
,用 - 0
把字符串转数值:
$('#jquery').on('click', e => e.target.innerHTML = e.target.innerHTML - 0 + 1);
这完全把 DOM 当“状态源”,简单直接,但没有数据层,后续可维护性与可组合性较弱。
AngularJS(作用域 + 指令)
ng-app="ngApp" ng-controller="buttonController"
声明应用与控制器,$scope.count
放状态,ng-click
触发 handleClick()
:
<div ng-app="ngApp" ng-controller="buttonController"><button ng-click="handleClick()">{{ count }}</button>
</div>
<script>
angular.module('ngApp', [])
.controller('buttonController', function($scope){$scope.count = 0;$scope.handleClick = function(){ $scope.count++; };
});
</script>
模板 {{ count }}
与 $scope
绑定,靠 digest 循环驱动更新。对这个微例子而言,样板代码相对偏多。
Vue(响应式 + 模板指令)
数据在 data
,事件在 methods
,点击只改数据、视图自动更新:
<div id="vue"><button v-on:click="handleClick">{{ count }}</button>
</div>
<script>
new Vue({el:'#vue',data:{ count:0 },methods:{ handleClick(){ this.count++; } }
});
</script>
结构清晰,体量中等。
React(状态 + 渲染函数)
使用 class 组件:
class ReactButton extends React.Component {state = { count: 0 };handleClick = () => this.setState({ count: this.state.count + 1 });render(){return <button onClick={this.handleClick}>{this.state.count}</button>;}
}
ReactDOM.render(<ReactButton/>, document.getElementById('react'));
状态在 this.state
,用 setState
触发重渲染。样板代码偏多(尤其在无需组件化时),但在大规模组件树、生态配套下优势明显。
dagger.js(原生 DOM 上的声明式指令,零样板)
一行 HTML 解决:
<button id="dagger" dg-cloak +load="{ count: 0 }" +click="count++">${ count }</button>
+load
初始化局部作用域(当前宿主元素)。${ count }
插值。+click
直接写表达式增量。dg-cloak
防止闪烁。
无需单独 JS 模块与挂载点,状态与视图强“同位(colocation)”。
方案对照(要点一览)
维度 | jQuery | AngularJS | Vue | React | dagger.js |
---|---|---|---|---|---|
状态归属 | DOM 即状态 | $scope | data | this.state | 元素局部作用域(+load ) |
事件绑定 | 代码里手绑 | ng-click | v-on:click | onClick | +click (表达式) |
视图更新 | 手改 DOM | digest | 响应式 | 重渲染 | 直接驱动 DOM(无 VDOM) |
样板/文件数 | 最少,但可维护性弱 | 偏多 | 适中 | 偏多 | 极少(常一行搞定) |
学习/心智 | 低,命令式 | 指令+作用域 | 模板+响应式 | 组件/状态/JSX | 原生 DOM + 轻指令 |
适配复杂度 | 组件化弱 | 过时生态 | 生态完善 | 生态最强 | 轻量、无构建亦可 |
注:上表依据本例的写法与常见范式总结。
技术观察与解读
-
“状态在哪里”决定复杂度走向
- jQuery:把 DOM 当真相源,最轻,但交互增多时难控。
- Vue/React:数据为真相源,模板/JSX 由状态驱动,易规模化。
- dagger.js:把“小状态”就地挂在元素作用域,天然就近,适合局部交互。
-
事件写法的表达力与可组合性
- Vue/Angular/React:声明事件名 → 调函数。
- dagger.js:
+click="count++"
直接表达更新意图,减少样板(无需methods
/this.setState
)。
-
引导方式与挂载点
- AngularJS:
ng-app
/ng-controller
。 - Vue:
new Vue({ el })
。 - React:
ReactDOM.render()
。 - dagger.js:元素上声明即可,不依赖全局挂载点,适合“就地增强”。
- AngularJS:
-
样板与可读性
- 本例中,从多到少大致是:React ≥ AngularJS ≥ Vue > jQuery ≈ dagger.js。
- 对“按钮 + 计数”这类微交互,dagger 的“一行式”复制扩散成本最低。
-
可规模化与生态
- 组件通信、路由、状态管理、SSR、工程化链路:React/Vue 生态占优。
- dagger.js:无需构建的轻交互、内嵌到现有页面、分散微部件;与 jQuery 渗透场景重叠,但更现代、声明式。
什么时候选谁?
- 给现有页面补几个小交互/小挂件:首选 dagger.js(或其他轻量范式),因为“就地声明 + 无样板”。
- 中大型单页应用:React / Vue 更稳,生态丰富、协作友好。
- 快速脚本式改动:jQuery 可用,但谨慎可维护性与状态一致性。
- AngularJS 存量:按存量维护;新项目不建议再启用 AngularJS。
附:关键代码片段
以下为本示例中各技术栈的最小可用版本,便于复制到自己的实验页面。
jQuery
<button id="jquery">0</button>
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script>
$('#jquery').on('click', e => e.target.innerHTML = e.target.innerHTML - 0 + 1);
</script>
AngularJS
<div ng-app="ngApp" ng-controller="buttonController"><button ng-click="handleClick()">{{ count }}</button>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<script>
angular.module('ngApp', [])
.controller('buttonController', function($scope){$scope.count = 0;$scope.handleClick = function(){ $scope.count++; };
});
</script>
Vue
<div id="vue"><button v-on:click="handleClick">{{ count }}</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
<script>
new Vue({el:'#vue',data:{ count:0 },methods:{ handleClick(){ this.count++; } }
});
</script>
React
<div id="react"></div>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script type="text/babel">
class ReactButton extends React.Component {state = { count: 0 };handleClick = () => this.setState({ count: this.state.count + 1 });render(){return <button onClick={this.handleClick}>{this.state.count}</button>;}
}
ReactDOM.render(<ReactButton/>, document.getElementById('react'));
</script>
dagger.js
<script type="module" crossorigin="anonymous" src="https://assets.codepen.io/5782383/dagger-1.0.0-RC.js" defer></script>
<button id="dagger" dg-cloak +load="{ count: 0 }" +click="count++">${ count }</button>
本文内容就到这里,后续文章将为大家带来更多案例和讲解。
如果对dagger.js感兴趣的话,请您点赞收藏、分享本系列文章,也欢迎留言或者私信作者提出问题和建议,您的关注是对我最大的支持和鼓励。感谢您的阅读,祝工作学习顺利!