React项目指导

原文链接:React项目详解GITHUB:react-webpack-config持续更新…React项目指导使用webpack需要安装的依赖webpack,webpack-cli,react,react-dombabel-loader,@babel/core,@babel/preset-env,@babel/preset-react设置.babelrc,{“presets”: ["@babel/preset-env","@babel/preset-react"]}设置scripts: “dev”: “webpack –mode development”, “build”: “webpack –mode production"设置webpack-dev-server:devServer: { compress: true, port: 9000, hot: true},“start”: “webpack-dev-server –config webpack.config.js” 设置performance:performance: { hints: false}Component基本组件let title = <h1>Hello, world!</h1>ReactDOM.render(title,document.getElementById(‘root’))动态组件import React from ‘react’;import ReactDOM from ‘react-dom’;let displayTime = () => { let nowTime = ( <div> <span>现在时间:{new Date().toLocaleTimeString()}</span> </div> ); ReactDOM.render(t nowTime, document.getElementById(‘root’) );};setInterval(displayTime, 1000);class组件构建器import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1>Hello,World!</h1> }}ReactDOM.render( <HelloTitle/>, document.getElementById(‘root’));props属性import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1>Hello,{this.props.name}!</h1> }}let titleDiv = ( <div> <HelloTitle name=“React”/> <HelloTitle name=“World”/> </div>);ReactDOM.render( titleDiv, document.getElementById(‘root’));props多层使用import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1>Hello,{this.props.name}!</h1> }}class HelloDiv extends Component { render() { return <div><HelloTitle name={this.props.name}/></div> }}ReactDOM.render( <HelloDiv name=“React”/>, document.getElementById(‘root’));组件复用import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1 style={this.props.style}>{this.props.content}</h1> }}class HelloDiv extends Component { render() { return <div> <HelloTitle content=“比较大的字” style={{‘fontSize’: 18}}/> <HelloTitle content=“比较小的字” style={{‘fontSize’: 12}}/> </div> }}ReactDOM.render( <HelloDiv/>, document.getElementById(‘root’));Component的状态state和生命周期state属性constructor(props) { super(props); this.state = { time: new Date().toLocaleTimeString() }}render() { return <h1>现在时间是{this.state.time}</h1>}组件构建完成后先执行的动作,componentDidMount():componentDidMount() { let upTime = () => { this.setState({time: new Date().toLocaleTimeString()}) }; setInterval(upTime, 1000)}setState()修改状态值this.setState({time: new Date().toLocaleTimeString()}) ...

April 10, 2019 · 1 min · jiezi

开发一个psotcss插件

git地址:开发一个psotcss插件节点类型postcss会将我们的css生成ast,然后会去遍历它,在遍历的过程中会传给我们一些不同类型的节点对象,我们主要需要了解的几个类型:css ast主要有3种父类型AtRule: @xxx的这种类型,如@screenComment: 注释Rule: 普通的css规则还有几个个比较重要的子类型:decl: 指的是每条具体的css规则rule:作用于某个选择器上的css规则集合这是test的地方的,不熟悉ast的可以先了解一下:css ast结构postCss操作方法postCss为我们提供了一些方便的操作方法遍历walk: 遍历所有节点信息,无论是atRule、rule、comment的父类型,还是rule、 decl的子类型walkAtRules:遍历所有的atRulewalkComments:遍历所有的注释节点walkDecls:遍历所以的属性walkRules:遍历所有的css代码块root.walkDecls(decl => { decl.prop = decl.prop.split(’’).reverse().join(’’);});postcss在遍历的过程中,会将当前遍历的对象的cell传给回调函数,该参数是对应的rule,decl或者comment等Constructor等构造函数的实例,根据遍历的节点不同,该实例可能会有如下属性:nodes: css规则的节点信息集合decl: 每条css规则的节点信息prop: 样式名,如widthvalue: 样式值, 如10pxtype: 类型source: 包括start和end的位置信息,start和end里都有line和* column表示行和列selector: type为rule时的选择器name: type为atRule时@紧接rule名,譬如@import ‘xxx.css’中的importparams: type为atRule时@紧接rule名后的值,譬如@import ‘xxx.css’中的xxx.csstext: type为comment时的注释内容同样还有继承的一些方法,给我操作css的, 比如操作每条具体css属性的declaration:afterbeforecleanRawsclonecloneAftercloneBeforeerrornextprevrawremovereplaceWithroottoStringwarnpostcss pluginpostcss插件如同babel插件一样,有固定的格式export default postcss.plugin(‘postcss-plugin-name’, function (opts) { opts = opts || {}; return function (root, result) { // 处理逻辑 };});注册个插件名,并获取插件配置参数opts返回值是个函数,这个函数主体是你的处理逻辑,有2个参数,一个是root,css ast的根节点。另一个是result,返回结果对象,譬如result.css,获得处理结果的css字符串,result.message包含组件里创建的warnings和自定义信息,result.warn()创造一个warning实例并将其加入到result.message中,result.warnings()获得所有创建过的warning。注意组件的异常信息处理,不要直接console,因为一些 postcss 处理器会过滤掉console的输出导致异常信息丢失,用node.warn或者node.error创造CssSyntaxError 实例,会自动带上源码中的位置信息帮助debug,加到异常信息队列里。直接调用postcss下的方法可以用postcss.parse来处理一段css文本,拿到css ast,然后进行处理,再通过调用toResult().css拿到处理后的css输出,在一些简单的处理中可以用这种方法。写在最后:想写一下的可以按照这里的指引,很详细https://github.com/postcss/postcss-plugin-boilerplate这是我写的一个将px转为vw的插件vw-by-px参考:http://api.postcss.orgpostcss-pixel-to-viewport

March 18, 2019 · 1 min · jiezi

[译] 使用 Proxy 来监测 Javascript 中的类

原文地址:Using Proxy to Track Javascript Class原文作者:Amir Harel译文出自:掘金翻译计划本文永久链接:https://github.com/xitu/gold-miner/blob/master/TODO1/using-proxy-to-track-javascript-class.md译者:SHERlocked93校对者:salomezhang, cyuamber使用 Proxy 来监测 Javascript 中的类Photo by Fabian Grohs on UnsplashProxy 对象(Proxy)是 ES6 的一个非常酷却鲜为人知的特性。虽然这个特性存在已久,但是我还是想在本文中对其稍作解释,并用一个例子说明一下它的用法。什么是 Proxy正如 MDN 上简单而枯燥的定义:Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。虽然这是一个不错的总结,但是我却并没有从中搞清楚 Proxy 能做什么,以及它能帮我们实现什么。首先,Proxy 的概念来源于元编程。简单的说,元编程是允许我们运行我们编写的应用程序(或核心)代码的代码。例如,臭名昭著的 eval 函数允许我们将字符串代码当做可执行代码来执行,它是就属于元编程领域。Proxy API 允许我们在对象和其消费实体中创建中间层,这种特性为我们提供了控制该对象的能力,比如可以决定怎样去进行它的 get 和 set,甚至可以自定义当访问这个对象上不存在的属性的时候我们可以做些什么。Proxy 的 APIvar p = new Proxy(target, handler);Proxy 构造函数获取一个 target 对象,和一个用来拦截 target 对象不同行为的 handler 对象。你可以设置下面这些拦截项:has — 拦截 in 操作。比如,你可以用它来隐藏对象上某些属性。get — 用来拦截读取操作。比如当试图读取不存在的属性时,你可以用它来返回默认值。set — 用来拦截赋值操作。比如给属性赋值的时候你可以增加验证的逻辑,如果验证不通过可以抛出错误。apply — 用来拦截函数调用操作。比如,你可以把所有的函数调用都包裹在 try/catch 语句块中。这只是一部分拦截项,你可以在 MDN 上找到完整的列表。下面是将 Proxy 用在验证上的一个简单的例子:const Car = { maker: ‘BMW’, year: 2018,};const proxyCar = new Proxy(Car, { set(obj, prop, value) { if (prop === ‘maker’ && value.length < 1) { throw new Error(‘Invalid maker’); } if (prop === ‘year’ && typeof value !== ’number’) { throw new Error(‘Invalid year’); } obj[prop] = value; return true; }});proxyCar.maker = ‘’; // throw exceptionproxyCar.year = ‘1999’; // throw exception可以看到,我们可以用 Proxy 来验证赋给被代理对象的值。使用 Proxy 来调试为了在实践中展示 Proxy 的能力,我创建了一个简单的监测库,用来监测给定的对象或类,监测项如下:函数执行时间函数的调用者或属性的访问者统计每个函数或属性的被访问次数。这是通过在访问任意对象、类、甚至是函数时,调用一个名为 proxyTrack 的函数来完成的。如果你希望监测是谁给一个对象的属性赋的值,或者一个函数执行了多久、执行了多少次、谁执行的,这个库将非常有用。我知道可能还有其他更好的工具来实现上面的功能,但是在这里我创建这个库就是为了用一用这个 API。使用 proxyTrack首先,我们看看怎么用:function MyClass() {}MyClass.prototype = { isPrime: function() { const num = this.num; for(var i = 2; i < num; i++) if(num % i === 0) return false; return num !== 1 && num !== 0; }, num: null,};MyClass.prototype.constructor = MyClass;const trackedClass = proxyTrack(MyClass);function start() { const my = new trackedClass(); my.num = 573723653; if (!my.isPrime()) { return ${my.num} is not prime; }}function main() { start();}main();如果我们运行这段代码,控制台将会输出:MyClass.num is being set by start for the 1 timeMyClass.num is being get by isPrime for the 1 timeMyClass.isPrime was called by start for the 1 time and took 0 mils.MyClass.num is being get by start for the 2 timeproxyTrack 接受 2 个参数:第一个是要监测的对象/类,第二个是一个配置项对象,如果没传递的话将被置为默认值。我们看看这个配置项默认值长啥样:const defaultOptions = { trackFunctions: true, trackProps: true, trackTime: true, trackCaller: true, trackCount: true, stdout: null, filter: null,};可以看到,你可以通过配置你关心的监测项来监测你的目标。比如你希望将结果输出出来,那么你可以将 console.log 赋给 stdout。还可以通过赋给 filter 的回调函数来自定义地控制输出哪些信息。你将会得到一个包括有监测信息的对象,并且如果你希望保留这个信息就返回 true,反之返回 false。在 React 中使用 proxyTrack因为 React 的组件实际上也是类,所以你可以通过 proxyTrack 来实时监控它。比如:class MyComponent extends Component{…}export default connect(mapStateToProps)(proxyTrack(MyComponent, { trackFunctions: true, trackProps: true, trackTime: true, trackCaller: true, trackCount: true, filter: (data) => { if( data.type === ‘get’ && data.prop === ‘componentDidUpdate’) return false; return true; }}));可以看到,你可以将你不关心的信息过滤掉,否则输出将会变得杂乱无章。实现 proxyTrack我们来看看 proxyTrack 的实现。首先是这个函数本身:export function proxyTrack(entity, options = defaultOptions) { if (typeof entity === ‘function’) return trackClass(entity, options); return trackObject(entity, options);}没什么特别的嘛,这里只是调用相关函数。再看看 trackObject:function trackObject(obj, options = {}) { const { trackFunctions, trackProps } = options; let resultObj = obj; if (trackFunctions) { proxyFunctions(resultObj, options); } if (trackProps) { resultObj = new Proxy(resultObj, { get: trackPropertyGet(options), set: trackPropertySet(options), }); } return resultObj;}function proxyFunctions(trackedEntity, options) { if (typeof trackedEntity === ‘function’) return; Object.getOwnPropertyNames(trackedEntity).forEach((name) => { if (typeof trackedEntity[name] === ‘function’) { trackedEntity[name] = new Proxy(trackedEntity[name], { apply: trackFunctionCall(options), }); } });}可以看到,假如我们希望监测对象的属性,我们创建了一个带有 get 和 set 拦截器的被监测对象。下面是 set 拦截器的实现:function trackPropertySet(options = {}) { return function set(target, prop, value, receiver) { const { trackCaller, trackCount, stdout, filter } = options; const error = trackCaller && new Error(); const caller = getCaller(error); const contextName = target.constructor.name === ‘Object’ ? ’’ : ${target.constructor.name}.; const name = ${contextName}${prop}; const hashKey = set_${name}; if (trackCount) { if (!callerMap[hashKey]) { callerMap[hashKey] = 1; } else { callerMap[hashKey]++; } } let output = ${name} is being set; if (trackCaller) { output += by ${caller.name}; } if (trackCount) { output += for the ${callerMap[hashKey]} time; } let canReport = true; if (filter) { canReport = filter({ type: ‘get’, prop, name, caller, count: callerMap[hashKey], value, }); } if (canReport) { if (stdout) { stdout(output); } else { console.log(output); } } return Reflect.set(target, prop, value, receiver); };}更有趣的是 trackClass 函数(至少对我来说是这样):function trackClass(cls, options = {}) { cls.prototype = trackObject(cls.prototype, options); cls.prototype.constructor = cls; return new Proxy(cls, { construct(target, args) { const obj = new target(…args); return new Proxy(obj, { get: trackPropertyGet(options), set: trackPropertySet(options), }); }, apply: trackFunctionCall(options), });}在这个案例中,因为我们希望拦截这个类上不属于原型上的属性,所以我们给这个类的原型创建了个代理,并且创建了个构造函数拦截器。别忘了,即使你在原型上定义了一个属性,但如果你再给这个对象赋值一个同名属性,JavaScript 将会创建一个这个属性的本地副本,所以赋值的改动并不会改变这个类其他实例的行为。这就是为何只对原型做代理并不能满足要求的原因。戳这里查看完整代码。如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。 ...

January 23, 2019 · 3 min · jiezi

vue观察模式浅析

以下是我对vue观察者模式的理解:github L6zt不要对框架的偏见, 你真的了解jquery、angular、react 等等,框架是什么只是工具而已。 你用过jquery的 trigger、on、off 事件绑定的方法吗?事实上 vue 不过也是这种模式,只不过vue 是自动调用on方法,自动触发trigger。甚至可以不用jquery对事件监听触发的实现。其实最终解释就是对某种事件的callback(基础原理)。以下是源码目录截图:1… vue 实例初始化时,会对data函数返回的对象里的属性调用以下方法,代码注释如下: // 这个是 vue 绑定自动绑定事件的方法和触发事件方法, 会把data函数返回的对象变量属性,重写对应属性的 赋值 和获取的操作。具体查看 (mdn Object.defineProperty api) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val // watcher 对象, 如果存在 if (Dep.target) { // 把Watcher 实例 推入 Dep 实例的 subs 数组里, 这个就相当于 on dep.depend() if (childOb) { childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare / if (newVal === value || (newVal !== newVal && value !== value)) { return } / eslint-enable no-self-compare */ if (process.env.NODE_ENV !== ‘production’ && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) // 通知 Dep 实例 中subs 里数组 中所有 Watcher 实例, 然后调用Watcher实例里的 update方法(), 这个就相当于 trigger。 dep.notify() } })// Watcher 构造函数 constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean )2…Watcher初始化时,会调用Dep.pushTarget方法, 把 Wathcer实例赋值到dep.js 里的Dep.target, 接着会根据 exporFn,运行exporFn 所代表的方法。这个方法里基本上包含调用 1…里的getter方法(想想render钩子里的操作基本有获取vue实例属性data里的值或者获取vue实例的计算属性的值)。var vm = new Vue({ data () { return {msg: ‘找个小姐姐!’} }, // 相当于 exporFn render(h) { return h(‘h3’, {}, // 这里面就会调用 msg 对应的 getter方法 this.msg ) }})所以就会使 render 函数 与 Vue 实例 的 数据 data属性 和观察属性等产生联系,这就形成一个闭环。当其中的属性变化,就会自动调用 setter 方法,从而触发dep.notify 方法,进而又会触发 dep.subs 里的 Watcher 实例调用 update方法,进而更新。(这部分代码不知如何说,故此没写, 具体查看源码) ...

September 24, 2018 · 2 min · jiezi