阿里云最近在作活动,低至2折,有兴趣能够看看:
https://promotion.aliyun.com/...
为了保证的可读性,本文采用意译而非直译。javascript
这是专门探索 JavaScript 及其所构建的组件的系列文章的第 19 篇。html
若是你错过了前面的章节,能够在这里找到它们:前端
想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!vue
Proxy 容许咱们建立一个对象的虚拟代理(替代对象),并为咱们提供了在访问或修改原始对象时,能够进行拦截的处理方法(handler),如 set()、get() 和 deleteProperty() 等等,这样咱们就能够避免很常见的这两种限制(vue 中):java
let proxy = new Proxy(target, habdler);
实例方法node
方法 | 描述 |
---|---|
handler.apply() | 拦截 Proxy 实例做为函数调用的操做 |
handler.construct() | 拦截 Proxy 实例做为函数调用的操做 |
handler.defineProperty() | 拦截 Object.defineProperty() 的操做 |
handler.deleteProperty() | 拦截 Proxy 实例删除属性操做 |
handler.get() | 拦截 读取属性的操做 |
handler.set() | 截 属性赋值的操做 |
handler.getOwnPropertyDescriptor() | 拦截 Object.getOwnPropertyDescriptor() 的操做 |
handler.getPrototypeOf() | 拦截 获取原型对象的操做 |
handler.has() | 拦截 属性检索操做 |
handler.isExtensible() | 拦截 Object.isExtensible() 操做 |
handler.ownKeys() | 拦截 Object.getOwnPropertyDescriptor() 的操做 |
handler.preventExtension() | 截 Object().preventExtension() 操做 |
handler.setPrototypeOf() | 拦截Object.setPrototypeOf()操做 |
Proxy.revocable() | 建立一个可取消的 Proxy 实例 |
Reflect 是一个内置的对象,它提供拦截 JavaScript 操做的方法。这些方法与处理器对象的方法相同。Reflect不是一个函数对象,所以它是不可构造的。react
与大多数全局对象不一样,Reflect没有构造函数。你不能将其与一个new运算符一块儿使用,或者将Reflect对象做为一个函数来调用。Reflect的全部属性和方法都是静态的(就像Math对象)。git
为何要设计 Reflect ?github
1. 更加有用的返回值web
早期写法:
try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure }
Reflect 写法:
if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }
2. 函数式操做
早期写法:
'name' in Object //true
Reflect 写法:
Reflect.has(Object,'name') //true
3. 可变参数形式的构造函数
通常写法:
var obj = new F(...args)
Reflect 写法:
var obj = Reflect.construct(F, args)
固然还有不少,你们能够自行到 MND 上查看
代理模式(Proxy),为其余对象提供一种代理以控制对这个对象的访问。代理模式使得代理对象控制具体对象的引用。代理几乎能够是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西。现实生活中的一个类比多是银行帐户的访问权限。
例如,你不能直接访问银行账户余额并根据须要更改值,你必需向拥有此权限的人(在本例中 你存钱的银行)询问。
var account = { balance: 5000 } var bank = new Proxy(account, { get: function (target, prop) { return 9000000; } }); console.log(account.balance); // 5,000 console.log(bank.balance); // 9,000,000 console.log(bank.currency); // 9,000,000
在上面的示例中,当使用 bank
对象访问 account
余额时,getter
函数被重写,它老是返回 9,000,000
而不是属性值,即便属性不存在。
var bank = new Proxy(account, { set: function (target, prop, value) { // Always set property value to 0 return Reflect.set(target, prop, 0); } }); account.balance = 5800; console.log(account.balance); // 5,800 bank.balance = 5400; console.log(account.balance); // 0
经过重写 set
函数,能够修改其行为。能够更改要设置的值,更改其余属性,甚至根本不执行任何操做。
如今已经对代理设计模式的工做方式有了基本心,让就开始编写 JavaScript 框架吧。
为了简单起见,将模拟 AngularJS 语法。声明控制器并将模板元素绑定到控制器属性:
<div ng-controller="InputController"> <!-- "Hello World!" --> <input ng-bind="message"/> <input ng-bind="message"/> </div> <script type="javascript"> function InputController () { this.message = 'Hello World!'; } angular.controller('InputController', InputController); </script>
首先,定义一个带有属性的控制器,而后在模板中使用这个控制器。最后,使用 ng-bind
属性启用与元素值的双向绑定。
要使属性绑定,须要得到一个控制器来声明这些属性, 所以,有必要定义一个控制器并将其引入框架中。
在控制器声明期间,框架将查找带有 ng-controller
属性的元素。
若是它符合其中一个已声明的控制器,它将建立该控制器的新实例,这个控制器实例只负责这个特定的模板。
var controllers = {}; var addController = function (name, constructor) { // Store controller constructor controllers[name] = { factory: constructor, instances: [] }; // Look for elements using the controller var element = document.querySelector('[ng-controller=' + name + ']'); if (!element){ return; // No element uses this controller } // Create a new instance and save it var ctrl = new controllers[name].factory; controllers[name].instances.push(ctrl); // Look for bindings..... }; addController('InputController', InputController);
这是手动处理的控制器变量声明。 controllers
对象包含经过调用 addController
在框架内声明的全部控制器。
对于每一个控制器,保存一个 factory
函数,以便在须要时实例化一个新控制器,该框架还存储模板中使用的相同控制器的每一个新实例。
如今,已经有了控制器的一个实例和使用这个实例的一个模板,下一步是查找具备使用控制器属性的绑定的元素。
var bindings = {}; // Note: element is the dom element using the controller Array.prototype.slice.call(element.querySelectorAll('[ng-bind]')) .map(function (element) { var boundValue = element.getAttribute('ng-bind'); if(!bindings[boundValue]) { bindings[boundValue] = { boundValue: boundValue, elements: [] } } bindings[boundValue].elements.push(element); });
上述中,它存储对象的全部绑的值定。该变量包含要与当前值绑定的全部属性和绑定该属性的全部 DOM 元素。
在框架完成了初步工做以后,接下就是有趣的部分:双向绑定。它涉及到将 controller
属性绑定到 DOM 元素,以便在代码更新属性值时更新 DOM。
另外,不要忘记将 DOM 元素绑定到 controller
属性。这样,当用户更改输入值时,它将更新 controller
属性,接着,它还将更新绑定到此属性的全部其余元素。
如上所述,Vue3 组件中经过封装 proxy
监听响应属性更改。 这里仅为控制器添加代理来作一样的事情。
// Note: ctrl is the controller instance var proxy = new Proxy(ctrl, { set: function (target, prop, value) { var bind = bindings[prop]; if(bind) { // Update each DOM element bound to the property bind.elements.forEach(function (element) { element.value = value; element.setAttribute('value', value); }); } return Reflect.set(target, prop, value); } });
每当设置绑定属性时,代理将检查绑定到该属性的全部元素,而后用新值更新它们。
在本例中,咱们只支持 input 元素绑定,由于只设置了 value
属性。
最后要作的是响应用户交互,DOM 元素在检测到值更改时触发事件。
监听这些事件并使用事件的新值更新绑定属性,因为代理,绑定到相同属性的全部其余元素将自动更新。
Object.keys(bindings).forEach(function (boundValue) { var bind = bindings[boundValue]; // Listen elements event and update proxy property bind.elements.forEach(function (element) { element.addEventListener('input', function (event) { proxy[bind.boundValue] = event.target.value; // Also triggers the proxy setter }); }) });
接着将学习了解决如何使用单 个HTML 文件运行 React,解释这些概念:functional component,函数组件, JSX 和 Virtual DOM。
React 提供了用组件构建代码的方法,收下,建立 watch
组 件。
<!-- Skipping all HTML5 boilerplate --> <script src="https://unpkg.com/react@16.2.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.2.0/umd/react-dom.development.js"></script> <!-- For JSX support (with babel) --> <script src="https://unpkg.com/babel-standalone@6.24.2/babel.min.js" charset="utf-8"></script> <div id="app"></div> <!-- React mounting point--> <script type="text/babel"> class Watch extends React.Component { render() { return <div>{this.props.hours}:{this.props.minutes}</div>; } } ReactDOM.render(<Watch hours="9" minutes="15"/>, document.getElementById('app')); </script>
忽略依赖项的 HTML 样板和脚本,剩下的几行就是 React 代码。首先,定义 Watch
组件及其模板,而后挂载React 到 DOM中,来渲染 Watch
组件。
咱们的 Wacth
组件很简单 ,它只展现咱们传给它的时和分钟。
你能够尝试修改这些属性的值(在 React中称为 props )。它将最终显示你传给它的内容,即便它不是数字。
const Watch = (props) => <div>{props.hours}:{props.minutes}</div>; ReactDOM.render(<Watch hours="Hello" minutes="World"/>, document.getElementById('app'));
props
只是经过周围组件传递给组件的数据,组件使用 props 进行业务逻辑和呈现。
可是一旦 props
不属于组件,它们就是不可变的(immutable)。所以,提供 props
的组件是可以更新props
值的惟一代码。
使用 props
很是简单,使用组件名称做为标记名称建立 DOM 节点。 而后给它以 props
名的属性,接着经过组件中的 this.props
能够得到传入的值。
注意到 render
函数返回的不带引号的 HTML, 这个使用是 JSX
语法,它是在 React 组件中定义 HTML 模板的简写语法。
// Equivalent to JSX: <Watch hours="9" minutes="15"/> React.createElement(Watch, {'hours': '9', 'minutes': '15'});
如今你可能但愿避免使用 JSX 来定义组件的模板,实际上,JSX 看起来像 语法糖。
如下代码片断,分别使用 JSX 和 React 语法以构建相同结果。
// Using JS with React.createElement React.createElement('form', null, React.createElement('div', {'className': 'form-group'}, React.createElement('label', {'htmlFor': 'email'}, 'Email address'), React.createElement('input', {'type': 'email', 'id': 'email', 'className': 'form-control'}), ), React.createElement('button', {'type': 'submit', 'className': 'btn btn-primary'}, 'Submit') ) // Using JSX <form> <div className="form-group"> <label htmlFor="email">Email address</label> <input type="email" id="email" className="form-control"/> </div> <button type="submit" className="btn btn-primary">Submit</button> </form>
最后一部分比较复杂,可是颇有趣,这将帮助你了解 React 底层的原理。
更新页面上的元素 (DOM树中的节点) 涉及到使用 DOM API。它将从新绘制页面,但可能很慢(请参阅本文了解缘由)。
许多框架,如 React 和 Vue.js 绕过了这个问题,它们提出了一个名为虚拟 DOM 的解决方案。
{ "type":"div", "props":{ "className":"form-group" }, "children":[ { "type":"label", "props":{ "htmlFor":"email" }, "children":[ "Email address"] }, { "type":"input", "props":{ "type":"email", "id":"email", "className":"form-control"}, "children":[] } ] }
想法很简单。读取和更新 DOM 树很是昂贵。所以,尽量少地进行更改并更新尽量少的节点。
减小对 DOM API 的调用及将 DOM 树结构保存在内存中, 因为讨论的是 JavaScript 框架,所以选择JSON 数据结构比较合理。
这种处理方式会当即展现了虚拟 DOM 中的变化。
此外虚拟 DOM 会先缓存一些更新操做,以便稍后在真正 DOM 上渲染,这个样是为了频繁操做从新渲染形成一些性能问题。
你还记得 React.createElement
吗? 实际上,这个函数做用是 (直接调用或经过 JSX 调用) 在 Virtual DOM 中 建立一个新节点。
要应用更新,Virtual DOM核心功能将发挥做用,即 协调算法,它的工做是提供最优的解决方案来解决之前和当前虚拟DOM 状态之间的差别。
原文:
https://medium.freecodecamp.o...
https://medium.freecodecamp.o...
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
你的点赞是我持续分享好东西的动力,欢迎点赞!
干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。
https://github.com/qq44924588...
我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,便可看到福利,你懂的。