摘要:线上另加入了排行榜功能,如需查看源码的,请切换到分支整个项目结构清晰,尤其单文件组件的表现力尤为突出,使得每个组件的逻辑都没有过于复杂,而且在的统筹下,的单向数据流模式使得所有的变化都在可控制可预期的范围内。
2016注定不是个平凡年,无论是中秋节问世的angular2,还是全面走向稳定的React,都免不了面对另一个竞争对手vue2。喜欢vue在设计思路上的“先进性”(原谅我用了这么一个词),敬佩作者尤小右本人的“国际范儿”,使得各框架之间的竞争略显妖娆(虽然从已存在问题的解决方案上看,各框架都有部分相似之处)。
因为vue2已经正式release,本教程做了一些修改(针对vue2)
所谓设计上的先进性,以下几点是我比较喜欢的:
数据驱动的响应式编程体验不同于AngularJS里基于digest cycle的脏检查机制,执行效率更高。内部基于Object.defineProperty特性做漂亮的hack实现(而且不支持IE8,大快人心)。更多细节,看这里
因为这个机制的出现,我们再也也不需要顾虑双向绑定的效率问题;亦或是像React那样搞什么immutability(对这块感兴趣可以看(译)JavaScript中的不可变性),因为Object.definePropery洞悉你的一切,妈妈再也不用担心你忘记实现shouldComponentUpdate了.
到这里你可能还不能体会vue的精妙,是时候来个栗子了!
假设我们有一个字段fullName,它依赖其他字段的变化,在AngularJS里,我们或许会用命令式这样写道:
$scope.user = { firstName: "", lastName: "" } $scope.fullName = "" //告诉程序主动“监视”user的变化,然后修改fullName的值 $scope.$watch("user", function(user) { $scope.fullName = user.firstName + " " + user.lastName }, true)
若是vue,改用声明式,写法如何?
data() { return { firstName: "", lastName: "" } }, computed: { fullName() { // 生命一个fullName的计算属性,并告诉程序它是由firstName和lastName组成。 // 至于具体是什么时候/如何完成数据拼装的,你就不用管了 return this.firstName + " " + this.lastName } }
相对于AngularJS里命令式的告诉框架,fullName一定要监视user对象的变化(注意里面还是deepWatch,效率更差),并且随之改变;vue以数据驱动为本质,声明式的定义fullName就是由firstName和lastName组成,无论怎么变化,都是如此。这种写法,更优雅有没有?
如果有兴趣看看用angular2如何实现相同的小游戏,走这里单文件组件模式
还在为一堆代码文件,到底哪个是JavaScript逻辑部分、哪个是css/less/sass样式部分、哪个是html/template模板部分;他们又该如何组织,怎么“编译”、如何发布?
有了单文件组件范式,配合webpack4(虽然文档依旧WIP),组件自包含,完美、没毛病!还有强大的开发工具支持,看着都赏心悦目,来个效果图:
用了这么多版面,说了一些好处,那么当我们真正需要面对一个应用,需要上规模开发时,vue又能带来怎样的变化呢?憋了几天,我想今天就写一个小游戏来试试整体感觉,先来看看我们今天的目标:
完整源码在这里:vue-memory-game
看了效果,知道源码在哪里了,那我们继续?
组件分解Break the UI into a component hierarchy,相信写过React的朋友对这句话都不陌生,在使用一种基于组件开发的模式时,最先考虑,而且也尤为重要的一件事,就是组件分解。下面我们看看组件分解示意图:
我们根据分解图,先把未来要实现的组件挨个儿列出来:
Game, 最外层的游戏面板
Dashboard, 上面的logo,游戏进度,最佳战绩的容器
Logo,左上角的logo
MatchInfo, 正中上方的游戏进度组件
Score, 右上角的最佳战绩组件
Chessboard, 正中大棋盘
Card, 中间那十六个棋牌
PlayStatus, 最下方的游戏状态信息栏
带薪搭环境(又来了?^^)#创建目录 mkdir vue-memory-game #创建一个package.json npm init #进入目录 cd vue-memory-game #安装开发环境依赖 npm install --save-dev babel-core babel-loader babel-plugin-transform-object-rest-spread babel-plugin-transform-runtime babel-preset-env css-loader file-loader html-webpack-plugin style-loader vue-hot-reload-api vue-html-loader vue-loader vue-style-loader vue-template-compiler webpack webpack-cli webpack-dev-server webpack-merge #安装运行时依赖 npm install vue vuex
这里开发环境依赖内容有点多,但不要害怕,大部分时候你不太关心里面的东西(当然,如果你要进阶,你要升职、加薪、迎娶白富美,那你最好搞清楚他们每一项都是什么东西)
另外在运行时依赖里不仅看到了vue,还看到了vuex。这又是个什么鬼?先不要慌,也别急着骂娘,我们来考虑一个问题,试想下,整个游戏按照上面分解的组件开发时,各个组件之间想必在逻辑上多少是有关系的,譬如:Card在Chessboard中的翻牌、配对,当然会影响到上方的Dashboard和下面的PlayStatus。那么“通信”,就成了待解决问题。
以前我们试图用事件广播来做,但随之而来的问题是,在应用不断的扩展、变化中,事件变得越来越复杂,越来越不可预料,以至于越来越难调试,越来越难追踪错误的root cause。这当然不是我们想要的,我们希望应用的各个部分都易维护、可扩展、好调试、能预测。
于是一种叫单向数据流的方式就冒了出来,用过React的人想必也不陌生,各组件的间的数据走向永远是单向、可预期的:
这当然也不是facebook的专利,都说vue牛逼了,那一定也有一个单向数据流的实现,就是我们这里用到的vuex。
掌握目录结构vue-memory-game ├── css │ └── main.css ├── img │ ├── ... │ └── zeppelin.png ├── js │ ├── components │ │ ├── card │ │ │ ├── Card.vue │ │ │ └── Chessboard.vue │ │ ├── dashboard │ │ │ ├── Dashboard.vue │ │ │ ├── Logo.vue │ │ │ ├── MatchInfo.vue │ │ │ └── Score.vue │ │ ├── footer │ │ │ └── PlayStatus.vue │ │ │ │ │ └── Game.vue │ │ │ ├── vuex │ │ ├── actions │ │ │ └── index.js │ │ ├── getters │ │ │ └── index.js │ │ ├── mutations │ │ │ └── index.js │ │ └── store │ │ ├── index.js │ │ └── statusEnum.js │ │ │ └── index.js │ ├── index.html_vm ├── package.json ├── webpack.config.js └── webpack.config.prod.js配置webpack
看了上面的文件目录结构图,要配置webpack,已经没有难度了,直接上代码:
const { resolve, join } = require("path") const HtmlWebpackPlugin = require("html-webpack-plugin") module.exports = { mode: "development", entry: { index: "./js/index.js" }, output: { filename: "[name].[hash].bundle.js", path: resolve(__dirname, "build") }, devtool: "#source-map", devServer: { contentBase: join(__dirname, "build"), compress: false, port: 8080, host: "0.0.0.0", hot: true, inline: true }, module: { rules: [ { test: /.vue$/, use: [ { loader: "vue-loader" } ], exclude: /node_modules/ }, { test: /.js$/, use: ["babel-loader"], exclude: /node_modules/ }, { test: /.css$/, use: ["style-loader", "css-loader"] }, { test: /.(png)$/, use: ["file-loader"] } ] }, resolve: { extensions: [".js", ".vue"] }, plugins: [ new HtmlWebpackPlugin({ filename: "index.html", inject: "body", template: "index.html_vm", favicon: "img/favicon.ico", hash: false }) ] }
我在这儿没有过多的涉及webpack的基本使用,反正webpack4的文档还在进行中,翻源码去吧(~逃)这里我们用了html-webpack-plugin里自动将编译后的bundle注入index.html_vm里,并生成最终的html。所以index.html_vm作为模板,我们也要先写出来:
touch index.html_vm
再将如下内容填入其中:
编写应用入口vue-memory-game
在webpack.config.js里,我们看到了
entry: { index: "./js/index.js" }
这也是本章整个vue应用的入口:
// 引入一些初始化的简单样式 import "../css/main.css" // 引入vue库 import Vue from "vue" // 引入本游戏核心入口组件 import Game from "./components/Game" // 引入状态管理机 import store from "./vuex/store" /* eslint-disable no-new */ new Vue({ el: "#application", render(h) { return h(Game) }, store })
本章代码本采用ES2015语法编写,譬如:components: {Game},相当于components: {Game: Game},这是enhanced-object-literals全局初始化样式我在这里没有过多介绍vue2的基本使用,不过我尽量列出可能涉及的知识点,便于学习
上面js/index.js里第一行就引用了全局初始化样式的css/main.css,我们就先把它写了吧:
* { box-sizing: border-box; padding: 0; margin: 0; } html, body { width: 100%; height: 100%; } body { display: flex; justify-content: center; align-items: center; }
本章大量使用flexbox来布局排版,不了解的可以学习一下(虽然我也是半吊子)
这段css/main.css之所以能被加载成功,多亏了webpack.config.js中的这段配置:
{ test: /.css$/, use: ["style-loader", "css-loader"] },
得利于css-loader和style-loader,上述css可以成功从index.js文件里引入,并被webpack处理到dom的标签里
第一个组件Game刚才的入口js/index.js里,我们注入了游戏主界面组件js/components/Game,下面就来创建它吧:
TBD...
单文件组件的魅力,到这里终于可以瞄一眼了,第一部分是模板,第二部分是逻辑,第三部分是样式。
这里上还有个scoped属性,表示样式仅对当前组件以及其子组件的模板部分生效。
单文件组件的加载由webpack.config.js中的配置:
{ test: /.vue$/, use: [ { loader: "vue-loader" } ], exclude: /node_modules/ },
所以我们可以在.vue文件中使用ES2015语法进行开发。
写了这么多,不运行一下,都说不过去了,现在请打开package.json文件,为其添加如下代码:
"scripts": { "start": "webpack-dev-server --hot --inline --host 0.0.0.0 --port 8080" }
然后在项目根目录调用:
#启动调试 npm start
浏览器访问:http://localhost:8080/,可以看到如下效果:
注意js/components/Game里的两个"TBD"部分,我们现在来补齐:
这里vuex/actions/index.js和vuex/store/statusEnum.js,我就不分别在这里写源码了,内容很简单,官网基本教程读完理解无障碍。
因为功能比较简单,大部分组件仅样式有差别,为了节省时间,我只挑一个最具代表性的components/card/Chessboard.vue来讲讲
components/card/Chessboard.vue写在最后,整体写完的效果,可以在这里把玩。
线上demo另加入了排行榜功能,如需查看源码的,请git checkout stage-1切换到stage-1分支
整个项目结构清晰,尤其单文件组件的表现力尤为突出,使得每个组件的逻辑都没有过于复杂,而且在vuex的统筹下,action -> mutation -> state的单向数据流模式使得所有的变化都在可控制、可预期的范围内。这点非常利于大型、复杂应用的开发。
另,vue2已经问世,对于之前跟着一起操作过vue版的朋友,发现源码里有疑惑的变更,请参考升级指南。
vue作为一个仅7000多行的轻量级框架而言,无论生态系统、社区、工具的发展都非常均衡、成熟,完全可以适应多业务场景以及稳定性需求。而且,vue2中对服务器端渲染的支持(而且是前所未有的流式支持),使得你不必再为单页应用的SEO问题、首屏渲染加速问题而担忧。欲知详情,看SSR
总的来说,2016年,vue让你的编程生涯,又多了一丝情怀(原谅我实在找不到什么好词儿了)。
如果关于代码有疑问,欢迎issue,也欢迎start
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79498.html
摘要:业界著名的开发框架,完美的设计以及组件化开发思想保证了框架的扩展性。面向生产环境的异步网络通信引擎。使开发人员可以编写高性能的异步并发,服务。通过简单的几行代码即可完成传统应用到的升级,给应用带来实打实的性能提升 Yii2:业界著名的开发框架,完美的OOP设计以及组件化开发思想保证了框架的扩展性。Swoole:面向生产环境的 PHP 异步网络通信引擎。使 PHP 开发人员可以编写高性能...
摘要:前言大概是我的业务领域比较狭窄的原因我总是会听说却很少在实际的开发中应用或者实践过它今天刚好看到高级程序设计第三版的数据存储部分说到了这里就对做一个深入访谈希望和我一样对似曾相识的朋友可以真正的熟悉并学会利用来服务我们的业务定义是服务器为了 前言 大概是我的业务领域比较狭窄的原因,我总是会听说cookie,却很少在实际的开发中应用或者实践过它,今天刚好看到的数据存储部分,说到了cook...
摘要:中立的云计算服务商优刻得作为参展商受邀出席,将展示在工业互联网领域的成熟解决方案及先进成果经验,推动新一代信息技术和工业制造业融合发展。成都一座来了就不想走的城市天府之国、美食之都、休闲之城…当工业的火遇上成都的辣又会擦出怎样的火花?当下工业互联网已成为数字经济发展、产业转型升级的重要引擎十四五开局之年更是提出:要培育形成具有国际影响力的工业互联网平台推进工业互联网+智能制造产业生态建设以工...
摘要:的网站仍然使用有漏洞库上周发布了开源社区安全现状报告,发现随着开源社区的日渐活跃,开源代码中包含的安全漏洞以及影响的范围也在不断扩大。与应用安全是流行的服务端框架,本文即是介绍如何使用以及其他的框架来增强应用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清单专注...
摘要:数据浪潮之间的前端工程师十年来,波澜壮阔的移动互联网浪潮促进了技术的迅猛发展,随着浏览器性能网络带宽等基础设施的提升,也能够承载起包含复杂交互可视化计算逻辑需求的富客户端应用。 showImg(https://segmentfault.com/img/remote/1460000016874425); 本文是架构师 2018-10 月刊的卷首语,归纳于自笔者的技术之路系列文章,也是对 ...
阅读 1945·2021-09-22 16:05
阅读 9114·2021-09-22 15:03
阅读 2856·2019-08-30 15:53
阅读 1683·2019-08-29 11:15
阅读 886·2019-08-26 13:52
阅读 2331·2019-08-26 11:32
阅读 1751·2019-08-26 10:38
阅读 2523·2019-08-23 17:19