Products
GG网络技术分享 2025-03-18 16:14 0
2013年末,我还在Google Creative Lab工作。当时在项目中使用了一段时间的Angular,在感叹数据绑定带来生产力提升的同时,我也感到Angular的API设计过于繁琐,使得学习曲线颇为陡峭。出于对Angular数据绑定原理的好奇,我开始 “造轮子”,自己实现了一个非常粗糙的、基于依赖收集的数据绑定库。这就是Vue.js的前身。同时在实际开发中,我发现用户界面完全可以用嵌套的组件树来描述,而一个组件恰恰可以对应MVVM中的ViewModel。于是我决定将我的数据绑定实验改进成一个真正的开源项目,其核心思想便是 “数据驱动的组件系统”。
MVVM的本质是通过数据绑定链接View和Model,让数据的变化自动映射为视图的更新。Vue.js在数据绑定的API设计上借鉴了Angular的指令机制:用户可以通过具有特殊前缀的HTML 属性来实现数据绑定,也可以使用常见的花括号模板插值,或是在表单元素上使用双向绑定:
<!-- 指令 -->
<span v-text=\"msg\"></span>
<!-- 插值 -->
<span>{{msg}}</span>
<!-- 双向绑定 -->
<input v-model=\"msg\">
插值本质上也是指令,只是为了方便模板的书写。在模板的编译过程中,Vue.js会为每一处需要动态更新的DOM节点创建一个指令对象。每当一个指令对象观测的数据变化时,它便会对所绑定的目标节点执行相应的DOM操作。基于指令的数据绑定使得具体的DOM操作都被合理地封装在指令定义中,业务代码只需要涉及模板和对数据状态的操作即可,这使得应用的开发效率和可维护性都大大提升。
图1 Vue.js的MVVM架构
与Angular不同的是,Vue.js的API里并没有繁杂的module、controller、scope、factory、service等概念,一切都是以“ViewModel 实例”为基本单位:
<!-- 模板 -->
<div id=\"app\">
{{msg}}
</div>
// 原生对象即数据
var data = {
msg: \'hello!\'
}
// 创建一个 ViewModel 实例
var vm = new Vue({
// 选择目标元素
el: \'#app\',
// 提供初始数据
data: data
})
渲染结果:
<div id=\"app\">
hello!
</div>
在渲染的同时,Vue.js也已经完成了数据的动态绑定:此时如果改动data.msg的值,DOM将自动更新。是不是非常简单易懂呢?除此之外,Vue.js对自定义指令、过滤器的API也做了大幅的简化,如果你有Angular的开发经验,上手会非常迅速。
Vue.js的数据观测实现原理和Angular有着本质的不同。了解Angular的读者可能知道,Angular的数据观测采用的是脏检查(dirty checking)机制。每一个指令都会有一个对应的用来观测数据的对象,叫做watcher;一个作用域中会有很多个watcher。每当界面需要更新时,Angular会遍历当前作用域里的所有watcher,对它们一一求值,然后和之前保存的旧值进行比较。如果求值的结果变化了,就触发对应的更新,这个过程叫做digest cycle。脏检查有两个问题:
Vue.js采用的则是基于依赖收集的观测机制。从原理上来说,和老牌MVVM框架Knockout是一样的。依赖收集的基本原理是:
Vue.js利用了ES5的Object.defineProperty方法,直接将原生数据对象的属性改造为getter和setter,在这两个函数内部实现依赖的收集和触发,而且完美支持嵌套的对象结构。对于数组,则通过包裹数组的可变方法(比如push)来监听数组的变化。这使得操作Vue.js的数据和操作原生对象几乎没有差别[注:在添加/删除属性,或是修改数组特定位置元素时,需要调用特定的函数,如obj.$add(key, value)才能触发更新。这是受ES5的语言特性所限。],数据操作的逻辑更为清晰流畅,和第三方数据同步方案的整合也更为方便。
图2 Vue.js的数据观测和数据绑定实现图解
图3 UI = 组件树
因此,在Vue.js的设计中将组件作为一个核心概念。可以说,每一个Vue.js应用都是围绕着组件来开发的。
注册一个Vue.js组件十分简单:
Vue.component(\'my-component\', {
// 模板
template: \'<div>{{msg}} {{privateMsg}}</div>\',
// 接受参数
props: {
msg: String<br>
},
// 私有数据,需要在函数中返回以避免多个实例共享一个对象
data: function {
return {
privateMsg: \'component!\'
}
}
})
注册之后即可在父组件模板中以自定义元素的形式调用一个子组件:
<my-component msg=\"hello\"></my-component>
渲染结果:
<div>hello component!</div>
Vue.js的组件可以理解为预先定义好了行为的ViewModel类。一个组件可以预定义很多选项,但最核心的是以下几个:
除此之外,同一颗组件树之内的组件之间还可以通过内建的事件API来进行通信。Vue.js提供了完善的定义、复用和嵌套组件的API,让开发者可以像搭积木一样用组件拼出整个应用的界面。这个思路的可行性在Facebook开源的React当中也得到了印证。
Vue.js的核心库只提供基本的API,本身在如何组织应用的文件结构上并不做太多约束。但在构建大型应用时,推荐使用Webpack+vue-loader这个组合以使针对组件的开发更高效。
Webpack是由Tobias Koppers开发的一个开源前端模块构建工具。它的基本功能是将以模块格式书写的多个JavaScript文件打包成一个文件,同时支持CommonJS和AMD格式。但让它与众不同的是,它提供了强大的loader API来定义对不同文件格式的预处理逻辑,从而让我们可以将CSS、模板,甚至是自定义的文件格式当做JavaScript模块来使用。Webpack 基于loader还可以实现大量高级功能,比如自动分块打包并按需加载、对图片资源引用的自动定位、根据图片大小决定是否用base64内联、开发时的模块热替换等等,可以说是目前前端构建领域最有竞争力的解决方案之一。
我在Webpack的loader API基础上开发了vue-loader插件,从而让我们可以用这样的单文件格式 (*.vue) 来书写Vue组件:
<style>
.my-component h2 {
color: red;
}
</style>
<template>
<div class=\"my-component\">
<h2>Hello from {{msg}}</h2>
<other-component></other-component>
</div>
</template>
<script>
// 遵循 CommonJS 模块格式
var otherComponent = require(\'./other-component\')
// 导出组件定义
module.exports = {
data: function {
return {
msg: \'vue-loader\'
}
},
components: {
\'other-component\': otherComponent
}
}
</script>
同时,还可以在*.vue文件中使用其他预处理器,只需要安装对应的Webpack loader即可:
<style lang=\"stylus\">
.my-component h2
color red
</style>
<template lang=\"jade\">
div.my-component
h2 Hello from {{msg}}
</template>
<script lang=\"babel\">
// 利用 Babel 编译 ES2015
export default {
data {
return {
msg: \'Hello from Babel!\'
}
}
}
</script>这样的组件格式,把一个组件的模板、样式、逻辑三要素整合在同一个文件中,即方便开发,也方便复用和维护。另外,Vue.js本身支持对组件的异步加载,配合Webpack的分块打包功能,可以极其轻松地实现组件的异步按需加载。
Vue.js还有几个值得一提的特性:
对Web Components有了解的读者看到这里可能会产生疑问:Vue.js的组件和Web Components的区别在哪里呢?这里简要地做一下分析。
Web Components是一套底层规范,本身并不带有数据绑定、动画系统等上层功能,因此更合适的比较对象可能是Polymer。Polymer在API和功能上和Vue.js比较相似,但它对Web Components的硬性依赖使得它在浏览器支持方面有一定的问题——在不支持Web Components规范的浏览器中,需要加载庞大的polyfill,不仅在性能上会有影响,并且有些功能,比如ShadowDOM,polyfill并没有办法完美支持。同时,Web Components规范本身尚未定稿,一些具体设计上仍存在不小的分歧。相比之下,Vue.js在支持的浏览器中(IE9+)没有任何依赖。
除此之外,在支持Web Components的环境中,我们也可以很简单地利用Web Components底层API将一个Vue.js组件封装在一个真正的自定义元素中,从而实现Vue.js组件和其他框架的无缝整合。
在发布之初,Vue.js原本是着眼于轻量的嵌入式使用场景。在今天,Vue.js也依然适用于这样的场景。由于其轻量(22kb min+gzip)、高性能的特点,对于移动场景也有很好的契合度。更重要的是,设计完备的组件系统和配套的构建工具、插件,使得Vue.js在保留了其简洁API的同时,也已经完全有能力担当起复杂的大型应用的开发。
从诞生起到现在的一年半历程中,Vue.js经历了一次彻底的重构,多次API的设计改进,目前已经趋于稳定,测试覆盖率长期保持在100%,GitHub Bug数量长期保持在个位数,并在世界各地都已经有公司/项目将Vue.js应用到生产环境中。在2015年晚些时候,Vue.js将发布1.0版本,敬请期待。
【参考链接】
Vue.js官方网站:http://vuejs.org
本文选自程序员电子版2015年8月A刊,该期更多文章请查看这里。2000年创刊至今所有文章目录请查看程序员封面秀。欢迎订阅程序员电子版(含iPad版、Android版、PDF版)。
欢迎加入CSDN前端交流群2:465281214,进行前端技术交流。
你也可以扫描下面二维码,加入CSDN前端大讲堂微信群,享受高含金量在线公开课的同时,还可与专家讲师在线切磋交流。(如果群满受限,你可加“Rachel_qg”为好友,并注明“参加CSDN前端大讲堂”,申请入群。)
近期CSDN前端大讲堂课程预告:
分享主题简介:React 带来的革命性创新是前端世界过去几年最激动人心的变化。自从接触 React 以来,我们深信 React 会彻底改变客户端开发者(包括前端、iOS 和 Android)的开发体验。本公开课中,将从四个大的方向:目标平台(targets)、数据处理(data)、工具(tools)和新的挑战,分享 React 生态系统和社区的进展和未来趋势。同时也将在线解答前端开发者在使用React过程中所面临的难点。
主讲人介绍:郭达峰,Strikingly.com CTO、联合创始人,具有多年前端开发经验。2010年开发了三款Facebook平台应用,获取了超过千万的用户。2012年创立了建站平台Strikingly,成为第一家进入YC孵化器的华人团队。Strikingly.com已使用React重写了大部分应用,是国内使用React比较多的公司之一。
1、扫码枪相当于键盘输入设备,输入一连串数字后加一个enter键。但在实际开发中需要区分是扫描枪输入还是键盘用户输入,区别在于扫码枪输入很快。
let code = \'\'; let lastTime, nextTime; let lastCode, nextCode; window.document.onkeypress = (e) => { if (window.event) { // IE nextCode = e.keyCode; } else if (e.which) { // Netscape/Firefox/Opera nextCode = e.which; } if (nextCode === 13) { if (code.length < 3) return; // 手动输入的时间不会让code的长度大于2,所以这里只会对扫码枪有 console.log(code); // 获取到扫码枪输入的内容,做别的操作 code = \'\'; lastCode = \'\'; lastTime = \'\'; return; } nextTime = new Date().getTime(); if (!lastTime && !lastCode) { code += e.key; } if (lastCode && lastTime && nextTime - lastTime > 30) { // 当扫码前有keypress事件时,防止首字缺失 code = e.key; } else if (lastCode && lastTime) { code += e.key; } lastCode = nextCode; lastTime = nextTime; } |
PS:下面看下js获取USB扫码枪数据的代码
前言
找了很多相关的教程不太好用,汲取各家之长总结精简了一下
原理
扫码枪扫描到的条形码每一位会触发一次onkeydown事件
比如扫描条码位‘1234567890\'的条形码,会连续执行10次onkeydown事件
条码扫描到最后一位,会直接触发Enter
需要引入jQuery,我这里用的是vue
window.onload = (e)=> { document.onkeydown = (e)=> { let nextCode,nextTime = \'\'; let lastTime = this.lastTime; let code = this.code; if (window.event) {// IE nextCode = e.keyCode } else if (e.which) {// Netscape/Firefox/Opera nextCode = e.which } nextTime = new Date().getTime(); //字母上方 数字键0-9 对应键码值 48-57; 数字键盘 数字键0-9 对应键码值 96-105 if((nextCode>=48&&nextCode<=57) || (nextCode>=96&&nextCode<=105)){ let codes = {\'48\':48,\'49\':49,\'50\':50,\'51\':51,\'52\':52,\'53\':53,\'54\':54,\'55\':55,\'56\':56,\'57\':57, \'96\':48,\'97\':49,\'98\':50,\'99\':51,\'100\':52,\'101\':53,\'102\':54,\'103\':55,\'104\':56,\'105\':57 }; nextCode = codes[nextCode]; nextTime = new Date().getTime(); } // 第二次输入延迟两秒,删除之前的数据重新计算 if(nextTime && lastTime && nextTime-lastTime>2000){ code = String.fromCharCode(nextCode); }else{ code += String.fromCharCode(nextCode) } // 保存数据 this.nextCode = nextCode; this.lastTime = nextTime; this.code = code; // 键入Enter if(e.which == 13) { // 判断 code 长度(这里就获取到条码值了,以下业务自由发挥) code = $.trim(code) if (code.length == 13) { this.$message(\'A类条码:\' + code); } else if (code.length == 23) { this.$message(\'B类条码:\' + code); } else if (code.length == 0) { this.$message(\'请输入条码\'); } else{ this.$message(\'条码不合法:\' + code); } //键入回车务必清空code值 this.code = \'\' return false; } } } |
以上就是一起看看js获取扫码枪输入数据的方法的详细内容,更多请关注网站的其它相关文章!
Demand feedback