Repository navigation
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 128 KB
/
Copy pathcontent.json
File metadata and controls
1 lines (1 loc) · 128 KB
1
{"meta":{"title":"1024子翔的程序人生","subtitle":null,"description":null,"author":"Choicelin","url":"https://choicelin.github.io"},"pages":[{"title":"","date":"2020-01-15T05:45:21.625Z","updated":"2018-01-21T07:46:56.000Z","comments":false,"path":"tags/index 下午1.44.10.html","permalink":"https://choicelin.github.io/tags/index 下午1.44.10.html","excerpt":"","text":""},{"title":"","date":"2020-01-15T05:45:21.628Z","updated":"2018-01-21T07:47:16.000Z","comments":false,"path":"categories/index.html","permalink":"https://choicelin.github.io/categories/index.html","excerpt":"","text":""}],"posts":[{"title":"Blockly设置预置块到代码区","slug":"Blockly设置预置块到代码区","date":"2020-06-18T09:52:19.000Z","updated":"2020-06-18T09:59:35.279Z","comments":true,"path":"2020/06/18/Blockly设置预置块到代码区/","link":"","permalink":"https://choicelin.github.io/2020/06/18/Blockly设置预置块到代码区/","excerpt":"","text":"123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148<!DOCTYPE html><html><head> <meta charset=\"utf-8\"> <title>Blockly Demo: Generating JavaScript</title> <script src=\"../../blockly_compressed.js\"></script> <script src=\"../../blocks_compressed.js\"></script> <script src=\"../../javascript_compressed.js\"></script> <script src=\"../../msg/js/en.js\"></script> <style> body { background-color: #fff; font-family: sans-serif; } h1 { font-weight: normal; font-size: 140%; } </style></head><body> <h1><a href=\"https://developers.google.com/blockly/\">Blockly</a> &gt; <a href=\"../index.html\">Demos</a> &gt; Generating JavaScript</h1> <p>This is a simple demo of generating code from blocks and running the code in a sandboxed JavaScript interpreter.</p> <p>&rarr; More info on <a href=\"https://developers.google.com/blockly/guides/configure/web/code-generators\">Code Generators</a> and <a href=\"https://developers.google.com/blockly/guides/app-integration/running-javascript\">Running JavaScript</a>.</p> <p> <button onclick=\"showCode()\">Show JavaScript</button> <button onclick=\"runCode()\">Run JavaScript</button> </p> <div id=\"blocklyDiv\" style=\"height: 480px; width: 600px;\"></div> <xml xmlns=\"https://developers.google.com/blockly/xml\" id=\"toolbox\" style=\"display: none\"> <category name=\"Logic\" colour=\"%{BKY_LOGIC_HUE}\"> <block type=\"controls_if\"></block> <block type=\"logic_compare\"></block> <block type=\"logic_operation\"></block> <block type=\"logic_negate\"></block> <block type=\"logic_boolean\"></block> </category> <category name=\"Loops\" colour=\"%{BKY_LOOPS_HUE}\"> <block type=\"controls_repeat_ext\"> <value name=\"TIMES\"> <block type=\"math_number\"> <field name=\"NUM\">10</field> </block> </value> </block> <block type=\"controls_whileUntil\"></block> </category> <category name=\"Math\" colour=\"%{BKY_MATH_HUE}\"> <block type=\"math_number\"> <field name=\"NUM\">123</field> </block> <block type=\"math_arithmetic\"></block> <block type=\"math_single\"></block> </category> <category name=\"Text\" colour=\"%{BKY_TEXTS_HUE}\"> <block type=\"text\"></block> <block type=\"text_length\"></block> <block type=\"text_print\"></block> </category> </xml> <xml xmlns=\"https://developers.google.com/blockly/xml\" id=\"startBlocks\" style=\"display: none\"> <block type=\"controls_if\" inline=\"false\" x=\"20\" y=\"20\"> <mutation else=\"1\"></mutation> <value name=\"IF0\"> <block type=\"logic_compare\" inline=\"true\"> <field name=\"OP\">EQ</field> <value name=\"A\"> <block type=\"math_arithmetic\" inline=\"true\"> <field name=\"OP\">MULTIPLY</field> <value name=\"A\"> <block type=\"math_number\"> <field name=\"NUM\">6</field> </block> </value> <value name=\"B\"> <block type=\"math_number\"> <field name=\"NUM\">7</field> </block> </value> </block> </value> <value name=\"B\"> <block type=\"math_number\"> <field name=\"NUM\">42</field> </block> </value> </block> </value> <statement name=\"DO0\"> <block type=\"text_print\" inline=\"false\"> <value name=\"TEXT\"> <block type=\"text\"> <field name=\"TEXT\">Don't panic</field> </block> </value> </block> </statement> <statement name=\"ELSE\"> <block type=\"text_print\" inline=\"false\"> <value name=\"TEXT\"> <block type=\"text\"> <field name=\"TEXT\">Panic</field> </block> </value> </block> </statement> </block> </xml> <script> var demoWorkspace = Blockly.inject('blocklyDiv', {media: '../../media/', toolbox: document.getElementById('toolbox')}); Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'), demoWorkspace); function showCode() { // Generate JavaScript code and display it. Blockly.JavaScript.INFINITE_LOOP_TRAP = null; var code = Blockly.JavaScript.workspaceToCode(demoWorkspace); alert(code); } function runCode() { // Generate JavaScript code and run it. window.LoopTrap = 1000; Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if (--window.LoopTrap == 0) throw \"Infinite loop.\";\\n'; var code = Blockly.JavaScript.workspaceToCode(demoWorkspace); Blockly.JavaScript.INFINITE_LOOP_TRAP = null; try { eval(code); } catch (e) { alert(e); } } </script></body></html> Blockly.Xml.domToWorkspace(document.getElementById(‘startBlocks’), demoWorkspace); 这个就是设置默认块显示在代码区域 如果需要用xml字符串可以使用Blockly.Xml.textToDom将xml string转化为dom,再传入Blockly.Xml.domToWorkspace第一个参数","categories":[],"tags":[{"name":"Google Blockly","slug":"Google-Blockly","permalink":"https://choicelin.github.io/tags/Google-Blockly/"}]},{"title":"mac安装ishowu audio capture后无法开机解决","slug":"mac安装ishowu-audio-capture后无法开机解决","date":"2019-12-14T02:59:54.000Z","updated":"2019-12-14T03:01:22.889Z","comments":true,"path":"2019/12/14/mac安装ishowu-audio-capture后无法开机解决/","link":"","permalink":"https://choicelin.github.io/2019/12/14/mac安装ishowu-audio-capture后无法开机解决/","excerpt":"","text":"删除Volume目录下的用户目录下的 Library/Extension 里面的ishowu开头的一个ketx文件即可 如有不懂,请联系413807584(qq)","categories":[],"tags":[{"name":"mac","slug":"mac","permalink":"https://choicelin.github.io/tags/mac/"}]},{"title":"mapbox做画多边形来进行地理位置信息查询","slug":"mapbox做画多边形来进行地理位置信息查询","date":"2019-09-16T03:49:47.000Z","updated":"2019-09-16T04:07:04.000Z","comments":true,"path":"2019/09/16/mapbox做画多边形来进行地理位置信息查询/","link":"","permalink":"https://choicelin.github.io/2019/09/16/mapbox做画多边形来进行地理位置信息查询/","excerpt":"","text":"mapbox介绍mapbox是一款国外的地图工具,这里介绍的是mapbox gl js api。是在浏览器中显示地图信息。 mapbox画多边形mapbox画多边形是使用了mapbox-gl-draw这个配套的库,这个库的api在初始化加上defaultMode为simple_select即可。可以禁用自定义控件,自己通过changeMode的方式来自定义控件。这里具体说一下,比如你要画图,你就按自己定义的按钮,调changeMode到画图模式上去,画完会自动回到默认的simple_select模式。 多变形地理位置信息查询的原理原理上是这样的,在通过mapbox-gl-draw的draw.create事件中能过获取到多边形的点的经纬度,这几个点的排列是能够绕出一块地理区域的,后端可以根据这个来把数据中的经纬度做一个对比,比如,有一种算法可以是获取最大的经度和最小的经度,最大的纬度和最小的纬度,然后在同时这两个范围内的数据返回给前端做展示即可。这个算法的精度还不够精确,这里只是举例。 授人以渔相信大家看了上面的思路,应该心中有底了吧。 warning注意,拉框查询可以使用官方的示例,拉框查询就是画矩形,是一比一还原上述算法,拉框查询无需使用mapbox-gl-draw这个库,详见官方示例。","categories":[],"tags":[{"name":"地图","slug":"地图","permalink":"https://choicelin.github.io/tags/地图/"}]},{"title":"Node.js异步演进过程","slug":"Node-js异步演进过程","date":"2019-07-28T03:24:12.000Z","updated":"2019-07-28T04:19:30.000Z","comments":true,"path":"2019/07/28/Node-js异步演进过程/","link":"","permalink":"https://choicelin.github.io/2019/07/28/Node-js异步演进过程/","excerpt":"","text":"Node.js是事件机制地处理请求中的io调用,这时就有了异步的概念出现,也就是说先去做其他事情,等io处理好了,再接受通知,并执行后续操作。下面我们来聊聊,Node.js里面怎么写异步的代码,异步流程控制概念。 首先异步的第一重境界是回调函数。Node常见的异步api都会接受一个函数参数,叫回调函数,也就是上面提到的接受通知后的后续操作都在这个回调函数中执行。对几个连续的异步操作的同步化就是异步流程控制。 回调函数示例 1234567fs.rename('/tmp/hello', '/tmp/world', (err) => { if (err) throw err; fs.stat('/tmp/world', (err, stats) => { if (err) throw err; console.log(`stats: ${JSON.stringify(stats)}`); });}); 上面rename的第三个参数以及stat的第二个参数均为回调函数。 express框架早期风格就是以回调的方式处理异步。 这种方式不太好的地方是当同步化的时候,就会导致代码嵌套层级过深,形成回调地狱。 回调地狱举例: 1234567891011121314151617181920fs.rename('/tmp/hello', '/tmp/world', (err) => { if (err) throw err; fs.rename('/tmp/world', '/tmp/hello', (err, stats) => { if (err) throw err; fs.rename('/tmp/hello', '/tmp/world', (err) => { if (err) throw err; fs.rename('/tmp/world', '/tmp/hello', (err, stats) => { if (err) throw err; fs.rename('/tmp/hello', '/tmp/world', (err) => { if (err) throw err; fs.rename('/tmp/world', '/tmp/hello', (err, stats) => { if (err) throw err; console.log('end') }); }); }); }); }); });}); 这样写的原因是保证每个执行流程是顺序的。然而造成了代码的可读性差。 这个时候Node.js出现了一个库叫co,这个库实现了一个叫generator的操作。让我们可以更好的控制异步的流程。这个库最后被应用于koa第一代中。 co库写法示例: 12345678co(function* () { var result = yield Promise.resolve(true); return result;}).then(function (value) { console.log(value);}, function (err) { console.error(err.stack);}); 这种写法有两个地方值得注意,一是function关键字后面有一个*,同时使用了yield关键字,这个*代表了这是个generator方法,有了这个*就可以方法体内使用yield关键字。yield关键字代表等待后面的操作执行完了以后才返回值,此时会造成挂起,这也是generator最初设计的本意(并不是完全为了解决异步的问题而产生,只是刚好能解决这个问题)。 提完了generator,顺便提一下现在真正使用的比较多的async方法,这个方法和generator在形式上的不同就是把*变成了async并放在function关键字的前面,然后yield变成了await,对代码的构成和产生的效果类似。 async方法示例: 12345router.post('/delete_topic', async (req, res, next) => { const { id } = req.body const result = await forumManager.deleteTopic({ id }) res.json(result)}) await后面跟的是一个Promise对象,也就是说forumManager.deleteTopic({ id })执行后会返回一个Promise对象。 什么是Promise对象呢,Promise有个A+规范,只要实现了这个规范的对象就是Promise对象。Promise也是用来做异步流程控制的。如果想知道Promise的应用可以看看相关的资料。 由于本人知识有限,可能有所疏漏之处,欢迎指正。","categories":[],"tags":[{"name":"异步","slug":"异步","permalink":"https://choicelin.github.io/tags/异步/"}]},{"title":"近况","slug":"近况","date":"2019-07-20T10:00:36.000Z","updated":"2019-07-20T11:48:04.000Z","comments":true,"path":"2019/07/20/近况/","link":"","permalink":"https://choicelin.github.io/2019/07/20/近况/","excerpt":"","text":"Node诞生已经10年了,可以说是和go同期的语言,18年我在狼叔的群里,大家都兴致勃勃的讨论着Node,感觉那个时候是我个人对Node最有冲动的一年,后面也陆续用express写了一些简单的api,觉得Node还是很有趣的。那一年,狼叔在写书,群友都三番五次的催稿,慢慢的我都觉得成了一个笑话。今年,他的书总算出来了,我很欣慰,虽然说实话,从目录上看,虽然很多讲的都是比较基础,但是还是下单了一本,想看看狼叔写的内容和其他作者写的是不是会不一样。 回顾接近3年的职业生涯,现在写前端代码的速度明显感到有所提升,这是唯一感觉渐入佳境的地方。3年我走了4家公司,在第三家公司武汉花卷科技有限公司,这里我认识了一个我职业生涯以来最大方和爽快的老板(包子赵长军),低调有内涵的且也为人爽快项目经理面窝,技术极客鱼丸,技术多面手栗子,还有很有阅历的豆丝,以及其他一群很有实力和头脑的同事。栗子和豆丝现在和我一起在啊哈编程,为青少年教育平台做自己的一份贡献。在这里我遇到另一个极客领导,技术总监张总。张总说他是007,从早上写代码到晚上12点,然后一周7天。当然我们是965工作制。其实我觉得张总是对技术有一种痴迷,他喜欢这个东西,很好玩。是的,确实有点好玩。张总那爆炸的开发的速度和解决实际技术问题的能力我就不多阐述了。 如果没有花卷,我是不会遇到这些人,和后面的故事的。我很感谢花卷的所有人,特别谢谢栗子,在花卷和啊哈都很照顾我,告诉了我很多网络和后端的知识甚至一些人生经验(蛙声一片),让我也能分清什么路由是走了前端怎么配nginx让它走后端等等。 离开花卷做了3份私活,也是花卷的一位同事介绍给我的,这位同事是一位设计,西瓜。西瓜的设计很独到,处理问题也是能从设计上解决技术上的问题,同时也很讲信用。和他合作还挺放心。 说了那么多其他人,现在再回到我自己,我自己在公司的业务开发中,扩展了一个开源的包的功能,发布了自己第一个,一个比较正式的开源npm包,虽然从最初的每周6百多下载量,跌到现在的30多,但是也见证了自己的开源的一小步。然后就是这个开源项目也成功申请了Jetbrain的开源许可证。本来职业生涯的前一年多都在做小程序和React,然而我现在敢说我自己的Vue比React用的好。这也是件很意料之外的事,React大部分是从别人那里学的,而Vue的全家桶,全是自己自学的。所以对知识在文档的哪个位置还是很有数的。在啊哈给自己的技术栈带来的收获,有Electron,Vue和Codorva,感觉也还挺简单的。唯一有些遗憾的是啊哈经历了一次裁员,很多熟悉的面孔再也看不到了。 9102下半年,准备把自己的全栈工坊小程序的后端由Java转Node,之所以这么做,是方便我个人维护。上半年开启了自己的直播生涯,会偶尔在b站上直播自己的代码周常,挺有意思的。B站搜索学酥兔即可找到我的直播间。 就到这里吧,人生不求大富大贵,只求能够平平安安,希望9102年下半年能够平平安安就好!","categories":[],"tags":[{"name":"程序心情","slug":"程序心情","permalink":"https://choicelin.github.io/tags/程序心情/"}]},{"title":"unmanaged hostwind设置ssh远程登录","slug":"unmanaged-hostwind设置ssh远程登录","date":"2019-05-02T02:04:49.000Z","updated":"2019-05-02T02:47:36.000Z","comments":true,"path":"2019/05/02/unmanaged-hostwind设置ssh远程登录/","link":"","permalink":"https://choicelin.github.io/2019/05/02/unmanaged-hostwind设置ssh远程登录/","excerpt":"","text":"介绍笔者最近想做一个node技术分享网站,使用了国外的hostwind服务器,这个网站准备使用php开发,虽然是node技术分享,然而使用php是不是有点搞人,不过没事,这不是这篇文章的关键,本文介绍了如何开启ssh远程登录功能及笔者踩到的一些坑。 Hostwind unmanged vps这个是我买的一款虚拟主机,问过客服后,unmanged的意思就是什么准备工作都得自己上,不是开箱即用的,这样正好符合了笔者的需求,毕竟也可以学习一波如何自己配置服务器和搭建php开发环境。于是支付订单后,笔者准备在命令行中使用ssh命令来连服务器。 第一个问题笔者遇到的第一个问题就是连接超时,第一个反应是会不会ip被屏蔽了,于是ping了一下ip,发现是可以的ping通的 命令入如下 1ping 192.39.21.251 这个ip是乱输的,实际输入的是我服务器的ip。 既然可以ping通,那么这个服务器是可以访问的,没有被屏蔽。 第二个反应是是不是没有安装ssh服务,于是在官网使用vnc登录服务器,检查了一下,发现是安装了的。 接着我修改了一下/etc/ssh/sshd_config文件,将端口修改为我想要的端口并开启javaboy这个用户登录 12Port 6666AllowUsers javaboy 然后在防火墙里添加该端口 1firewall-cmd --zone=public --add-port=6666/tcp --permanent 防火墙firewall需要安装 添加用户并设置密码 12adduser -m javaboypasswd javaboy 重启ssh服务 1systemctl restart sshd.service 第二个问题connection closed by foreign host 修改sshd_config 12ClientAliveInterval 60ClientAliveCountMax 3 第三个问题port 6666 conection closed 这个问题解决方式是重载firewall让其修改生效 1firewall-cmd --reload 最后在本地 1ssh -p 6666 javaboy@192.39.21.251 如果需要访问root用户 将sshd_config中PermitRootLogin设置如下 1PermitRootLogin yes 再使用 1ssh -p 6666 root@192.39.21.251 即可","categories":[],"tags":[{"name":"centos7","slug":"centos7","permalink":"https://choicelin.github.io/tags/centos7/"}]},{"title":"React Native填坑系列2---react navigation","slug":"React-Native填坑系列2-react-navigation","date":"2019-04-14T03:59:08.000Z","updated":"2019-04-14T05:59:30.000Z","comments":true,"path":"2019/04/14/React-Native填坑系列2-react-navigation/","link":"","permalink":"https://choicelin.github.io/2019/04/14/React-Native填坑系列2-react-navigation/","excerpt":"","text":"引言继上期填坑后,笔者继续推进安卓app的开发,web上都有导航路由的功能,app要实现这个,需要模拟类似web导航的特性,目前有一个静态路由的库比较好,叫react-navigation,于是笔者开启了第二段填坑之旅。 关于要实现的路由形式在准备阶段,我们大多数都会想尽可能的覆盖很多情况,所以笔者也是,关于路由的构成,首先有鉴权,然后有底部导航,接着每层导航对应的页面组件有内页,同时内页不会有底部导航。这是笔者目前的一个形式。 React Navigation简介就像引言提到的这是在app中模拟的一个导航历史功能的库。浏览器的导航历史维持在一个堆栈中,每次的后退就是出栈,前进就是进栈,栈顶就是当前显示的页面。React Navigation除了提供了这些基础的功能以外,还提供了手势和动画的效果。 如何实现这种路由形式首先React Native和web中的单页应用(Vue SPA和React SPA等)有些相似,都会有一个根视图,然后向下包含内容。比如在Vue中的 12345new Vue({ router, store, render: h => h(App)}).$mount('#app') 这里的根就是id为app的节点。 而在React Native中也有类似的行为。 所以第一件事情就是确定这个根视图。根据react-navigation官方推荐的鉴权是使用createSwitchNavigator。由于用户进来第一件事情对开发者来说,笔者认为是鉴权,也就是确定用户是否有权限进入一些页面,同时确定用户会进哪里,所以笔者将该组件作为app的根视图。 createSwitchNavigator是什么?下面引用官方的一段介绍: The purpose of SwitchNavigator is to only ever show one screen at a time. By default, it does not handle back actions and it resets routes to their default state when you switch away. 笔者翻译:SwitchNavigator的目的在于在同一时间下仅显示一个页面。它的默认行为: 不处理返回动作,可以理解为点不了返回,无需担心还会返回到了之前的页面 其正在显示的页面会重置为其最初的设置,也就是重新加载了对应的页面,会重头初始化 SwitchNavigator如其名称一样,是在多个视图之间切换(switch),那么我们的根视图下面就会有子视图的显示切换,这些切换的视图如何定义呢? 定义根视图的子切换视图talk is cheap,show me the code.好,那么就上代码。 / 指代项目根目录,本项目由react-native-cli搭建 12345678910111213141516/** * /routes/AppNavigator.js */import {createSwitchNavigator} from 'react-navigation'// ....省略部分代码export default createSwitchNavigator( { AuthPage: Auth, Pages: PageStack }, { initialRouteName: 'AuthPage', }) 123456789/** * /App.js */import React, {Component} from 'react';import {createAppContainer} from 'react-navigation'import AppNavigator from './routes/AppNavigator'export default createAppContainer(AppNavigator) 在AppNavigator.js中可以看到createSwitchNavigator中有两个参数,第二个参数里面的initialRouteName是指默认显示的视图(已切换上的视图),第一个参数里面配置的是可供切换的视图,AuthPage和Pages是指的切换的视图的名字,后面跟的值是实际的Navigator对象或者Component,说人话就是实际的视图对象,目前笔者用到的视图对象是上述提到的两种,Navigator对象是createStackNavigator和createBottomTabNavigator等创建出来的,而Compnenent则是指React创建的视图组件。当然,现在这段有点奇怪,因为Auth和PageStack都还没创建和引入,它们在省略的代码里,所以莫慌,我们接下来,慢慢实现省略的代码。 Auth Component下面笔者将会定义上文提到Auth,它是一个React Component,由本文仅介绍路由的实现形式,具体鉴权代码是没有的。但是鉴权的逻辑会放在这个组件里。 12345678910111213141516171819202122232425262728293031/** * /views/Auth.js */import React, {Component} from 'react'import {View, Text, StyleSheet} from 'react-native'import Icon from 'react-native-vector-icons/AntDesign'export default class Auth extends Component { render () { return ( <View style={[styles.fc, styles.flex1]}> <Text style={styles.f38}>auth</Text> <Icon name=\"yuque\" size={30} color=\"#004aff\"/> </View> ) }}const styles = StyleSheet.create({ fc: { alignItems: 'center', justifyContent: 'center' }, flex1: { flex: 1 }, f38: { fontSize: 38 }}) 这就是Auth的一个静态的壳,下面笔者在/routes/AppNavigator.js中引入该组件 1234567891011121314151617/** * /routes/AppNavigator.js */import {createSwitchNavigator} from 'react-navigation'import Auth from '../views/Auth' // 新增的代码// ....省略部分代码export default createSwitchNavigator( { AuthPage: Auth, Pages: PageStack }, { initialRouteName: 'AuthPage', }) PageStack Navigator上文中引入了Auth组件,笔者的路由又进了一步。那么现在来看下这个PageStack怎么做。还记得最初想的路由形式吗? 有鉴权和底部导航还有内页。这里AuthPage就是鉴权,所以逻辑可以梳理一下,首先用户进来会显示initialRouteName提供的AuthPage视图,即/views/Auth.js组件展示的内容;接着通过Auth内的判断是否有后端的token后,如果有token,会跳转到Pages视图,显示PageStack的内容,如果没有token,那么我们可以再加一个视图,跳转过去。但是目前笔者准备暂时将其停留在原地。需要注意的是,跳转的代码和判断的逻辑还木有加上,笔者需要先完成现在路由的构建。当我们进入PageStack后,底部导航就需要显示出来,然后显示对应的页面,并备好内页(即当前导航的页面的深层页面)。下面笔者来添加点代码: 123456789101112131415161718192021222324252627282930313233343536373839/** * /routes/AppNavigator.js */import { createSwitchNavigator, createStackNavigator} from 'react-navigation'import Auth from '../views/Auth' import MyTests from '../views/MyTests' // 新增的代码import TestDetail from '../views/TestDetail' // 新增的代码// ....省略部分代码/** * 新增的代码 */const PageStack = createStackNavigator({ MyTests: { screen: MyTests // Component }, TestDetail: { screen: TestDetail // Component }, Bottom: { screen: BottomNavigator }}, { initialRouteName: 'Bottom'})export default createSwitchNavigator( { AuthPage: Auth, Pages: PageStack }, { initialRouteName: 'AuthPage', }) 上述代码注明Component的都是指和Auth类似的React组件,它们的定义可以仿照Auth的定义来实现,只是没有了鉴权逻辑。同时这里的两个Component指的就是上文中的内页。 createStackNavigator上面的代码用到了一个方法叫createStackNavigator,这个方法即创建页面路由栈的方法,第一个参数是包含的路由,第二参数里面的initialRouteName指定这个Navigator视图激活后的默认显示的Navigator或者Component(激活是指当前视图显示的是该Navigator) BottomNavigator下面我们来定义底部导航,在前端里有个专业术语叫tabBar,指的就是这个。这个是一个Navigator对象,通过createBottomTabNavigator方法来创建 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172/** * /routes/AppNavigator.js */import React from 'react'import {Platform} from 'react-native'import {createBottomTabNavigator, createStackNavigator, createSwitchNavigator} from 'react-navigation'import Icon from 'react-native-vector-icons/AntDesign'import Home from '../views/Home' // Componentimport User from '../views/User' // Componentimport Auth from '../views/Auth' // Componentimport MyTests from '../views/MyTests' // Componentimport TestDetail from '../views/TestDetail' // Componentconst BottomNavigator = createBottomTabNavigator({ Home: { screen: Home, navigationOptions: { tabBarLabel: '测验', tabBarIcon: ({tintColor, focused}) => ( <Icon name=\"calculator\" color={tintColor} size={26} /> ) } }, User: { screen: User, navigationOptions: { tabBarLabel: '个人', tabBarIcon: ({tintColor, focused}) => ( <Icon name=\"user\" color={tintColor} size={26} /> ) } }}, { tabBarOptions: { activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#004aff', }})const PageStack = createStackNavigator({ MyTests: { screen: MyTests }, TestDetail: { screen: TestDetail }, Bottom: { screen: BottomNavigator }}, { initialRouteName: 'Bottom'})export default createSwitchNavigator( { Auth, Pages: PageStack }, { initialRouteName: 'Pages', }) 上述代码中的Component跟PageStack Navigator提到的Component注释是同一个意思。 createBottomTabNavigator这个方法用来创建一个底部导航,显示效果如下图: (用长方形圈起来的部分是tabbar,用椭圆圈起来的部分是tabbar激活的部分承载的视图) createBottomTabNavigator有两个参数,第一个参数是配置视图有多少个,即承载的视图和下方的tabbar文字和图标,第二个参数时是对第一个参数里的视图,配置公共的属性,比如这里用到的activeTintColor是指的tabbar激活(即被选中)时的颜色,这里的#004aff是蓝色,Platform.OS是用来是区分是iOS平台还是安卓平台,笔者展示的图片是安卓平台,所以读者看到的激活状态是蓝色的。 结语到这里,其实整个路由的形式已经做好了,接下来就是对它进行添砖加瓦,或者进行更进一步的扩展,目前笔者的需求暂时只会用到这些,后期会根据新的需求做调整。 最后,感谢大家的观看,欢迎使用全栈工坊微信小程序并关注java技术大本营微信公众号。","categories":[],"tags":[{"name":"React Native","slug":"React-Native","permalink":"https://choicelin.github.io/tags/React-Native/"}]},{"title":"React Native填坑系列之一(Could not GET 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.2.1/gradle-3.2.1.pom'. > Connect to 127.0.0.1:1089 [/127.0.0.1] failed: Connection refused (Connection refused))","slug":"React-Native填坑系列之一(lCould-not-GET-https-dl-google-com-dl-android-maven2-com-android-tools-build-gradle-2-2-2-gradle-2-2-2-pom-Connect-to-127-0-0-1-1089-127-0-0-1-failed-Connection-refused-Connection-refused-)","date":"2019-04-08T05:16:32.000Z","updated":"2019-04-08T08:05:20.000Z","comments":true,"path":"2019/04/08/React-Native填坑系列之一(lCould-not-GET-https-dl-google-com-dl-android-maven2-com-android-tools-build-gradle-2-2-2-gradle-2-2-2-pom-Connect-to-127-0-0-1-1089-127-0-0-1-failed-Connection-refused-Connection-refused-)/","link":"","permalink":"https://choicelin.github.io/2019/04/08/React-Native填坑系列之一(lCould-not-GET-https-dl-google-com-dl-android-maven2-com-android-tools-build-gradle-2-2-2-gradle-2-2-2-pom-Connect-to-127-0-0-1-1089-127-0-0-1-failed-Connection-refused-Connection-refused-)/","excerpt":"","text":"为什么开始React Native开发和朋友一起准备给网站配套做一款android app。经过宋小菜的scott老师在他文章中对宋小菜架构的介绍,其中提到了对React Native的使用,让我重拾信心,再加上之前有过相关经验,于是决定使用它来进行app的开发。 第一个bug当我装好android studio并把项目初始化好了以后,我自信满满的运行react-native run-android,扎心的错误铺面而来 12345678910111213141516171819202122232425262728293031323334353637383940JS server already running.Building and installing the app on the device (cd android && ./gradlew installDebug)...FAILURE: Build failed with an exception.* What went wrong:A problem occurred configuring root project 'idea_app'.> Could not resolve all artifacts for configuration ':classpath'. > Could not resolve com.android.tools.build:gradle:3.2.1. Required by: project : > Could not resolve com.android.tools.build:gradle:3.2.1. > Could not get resource 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.2.1/gradle-3.2.1.pom'. > Could not GET 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.2.1/gradle-3.2.1.pom'. > Connect to 127.0.0.1:1089 [/127.0.0.1] failed: Connection refused (Connection refused) > Could not resolve com.android.tools.build:gradle:3.2.1. > Could not get resource 'https://jcenter.bintray.com/com/android/tools/build/gradle/3.2.1/gradle-3.2.1.pom'. > Could not GET 'https://jcenter.bintray.com/com/android/tools/build/gradle/3.2.1/gradle-3.2.1.pom'. > Connect to 127.0.0.1:1089 [/127.0.0.1] failed: Connection refused (Connection refused)* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 0sCould not install the app on the device, read the error above for details.Make sure you have an Android emulator running or a device connected and haveset up your Android development environment:https://facebook.github.io/react-native/docs/getting-started.htmlCommand failed: ./gradlew installDebugError: Command failed: ./gradlew installDebug at checkExecSyncError (child_process.js:616:11) at Object.execFileSync (child_process.js:634:13) at runOnAllDevices (/Users/kiralin/lixiang/idea_app/node_modules/react-native/local-cli/runAndroid/runAndroid.js:299:19) at buildAndRun (/Users/kiralin/lixiang/idea_app/node_modules/react-native/local-cli/runAndroid/runAndroid.js:135:12) at isPackagerRunning.then.result (/Users/kiralin/lixiang/idea_app/node_modules/react-native/local-cli/runAndroid/runAndroid.js:65:12) at process._tickCallback (internal/process/next_tick.js:68:7) 注明一下,我使用的RN版本是0.58.6是58系列最后一个版本。于是想到的第一件事情就是谷歌。最早我搜索的是Could not resolve com.android.tools.build:gradle:3.2.1.,结果发现谷歌到的第一个就是在build.gradle中加入google()方法,然而当我打开我的build.gradle文件,我发现RN官方已经加入了该方法,我顿时感到很诧异,这一招怕是行不通了。其他回答里面还有加入maven配置的,基本上也都行不通。 转机通过仔细观察报错的栈,发现最底层的才是核心的错误,于是我搜索127.0.01:1089相关的RN回答,于是找到了一个方法,那就是将RN里的android文件夹在android studio里面打开,让android studio来帮你把依赖安好,我一看内心就觉得靠谱。 But,在AS中connection refused。于是我分析是代理的问题,恰巧吃饭了,询问了一下同事代理方面的问题,提到了端口占用,我回来检测了一下,并没有该情况,不过还好,我基本也有把握,因为这种问题应该是安卓常见问题, 接着谷歌,AS中connection refused,果然第二个答案就solve了我的problem。 下面贴一下链接解决studio Connection refused (Connection refused) 解决这个问题后,于是我react-native run-android,成功build,然后在avd中出现欢迎界面。 结语这是RN填坑第一弹,后期有坑会继续填满","categories":[],"tags":[{"name":"React Native","slug":"React-Native","permalink":"https://choicelin.github.io/tags/React-Native/"}]},{"title":"2019前端windows环境搭建指南","slug":"2019前端windows环境搭建指南","date":"2019-02-21T07:45:42.000Z","updated":"2019-02-21T08:11:08.000Z","comments":true,"path":"2019/02/21/2019前端windows环境搭建指南/","link":"","permalink":"https://choicelin.github.io/2019/02/21/2019前端windows环境搭建指南/","excerpt":"","text":"前言2019年写的这篇开发环境搭建指南,本文旨在说明最基础的环境,关于项目搭建将不做涉及。 Node.js官网 下载LTS版本安装即可。 安装成功后会默认安装上npm,npm是node的包管理工具,npm的源连接的是国外,国内使用会比较慢,所以需要替换一下源,打开cmd(windows命令行提示),输入下列命令回车即可 1npm config set registry http://registry.npm.taobao.org/ YarnYarn是可以代替npm作为包的管理,使用方式跟npm有点像,包安装问题上会少一点 下载安装地址:Yarn官网 Python环境sass可能会依赖到 官网 3.7和2.7都下载安装吧 Git官网 下载安装即可 安装以后可以使用右键开启git bash,这是一个命令行窗口,以后就可以用这个替代cmd 配置ssh key 找到git安装的目录,比如D:\\Program Files\\Git\\usr\\bin 在命令行中cd到这个目录下,运行下面命令 下面的引号内,填你的邮箱,然后一路回车即可完成 1ssh-keygen -t rsa -C \"youremail@example.com\" IDE和编辑器编辑器百度VSCode IDE百度WebStorm(请支持正版,一个月$5.9,约40RMB,支持微信支付宝,按年买有优惠) 免费才是最贵的 总结ok,就到这里了,大家加油!","categories":[],"tags":[{"name":"开发环境","slug":"开发环境","permalink":"https://choicelin.github.io/tags/开发环境/"}]},{"title":"加油,小林","slug":"加油,小林","date":"2019-01-23T12:16:09.000Z","updated":"2019-01-23T12:38:10.000Z","comments":true,"path":"2019/01/23/加油,小林/","link":"","permalink":"https://choicelin.github.io/2019/01/23/加油,小林/","excerpt":"","text":"最近频繁感到成年人的生活不易,晚上吃饭看到央视节目越战越勇,一个在网上被广泛传播的外卖哥痛哭的视频的主角参加了这个节目。看到那个痛哭流涕的视频,我觉得很难受,杨帆问他,“你为什么哭了?”。他说晚上1点送外卖,孩子发烧了,回去给孩子买药,耽误了送外卖,一天几单都白费了,一时控制不住,就哭了。这个男人描述得轻描淡写,实际情况是,他的小孩染上了白血病,为了照顾儿子,只好选择做外卖,这样既可以照顾家人,又可以补贴家用。他从6点起来,一直送到晚上1点。杨帆送了他的孩子们一人一个奥特曼,杨帆说:“奥特曼是超人,其实有时候超人不一定是奥特曼,爸爸也是你们的超人。” 还有一位29岁的女选手,得了白塞病,身材一度胖到150斤,被迫离开了文工团。白塞病是一种眼疾,症状是眼睛内有大量絮状和黑色物体,严重情况甚至会失明。她在治疗的过程中,遇到了一位大哥,那位大哥说:“你看过夜晚石景山的星星吗?”。她非常疑惑,后来她得知白塞病在夜晚时,眼睛的黑色物体会和夜晚的夜色融为一体就好像自己没有得过这个病一样。于是,她和她的男友一起去看了石景山的夜空,当星星出来的那一刹那,她泪流满面。她曾经问她的男友,自己变得又胖又丑,你还会喜欢我吗。很幸运的是,她的男友用行动告诉她,yes。她的男友在节目中对她求婚成功。 今天我爸对我说,你晚上说梦话,说的很清楚,你还记得吗?我说不记得了,怎么了。他说,你现在是不是有压力。我本来没想这个问题,突然这么一说,浑身都不自在,真的好像有点压力一样。 不过,这就成年人的世界,大家都承受着自己的压力和难受,坚强地向前进步。无论如何,艰难的日子都会过去,我们要看到光明的明天。想到这里,我觉得即使有点压力,也能够承受。并且我要像豆丝的钉钉签名一样,好好工作,好好做人才是王道。 与大家共勉,加油,小林!","categories":[],"tags":[{"name":"心情","slug":"心情","permalink":"https://choicelin.github.io/tags/心情/"}]},{"title":"CSS布局","slug":"CSS布局","date":"2019-01-12T04:18:30.000Z","updated":"2019-01-12T06:41:36.000Z","comments":true,"path":"2019/01/12/CSS布局/","link":"","permalink":"https://choicelin.github.io/2019/01/12/CSS布局/","excerpt":"","text":"左右布局 使用浮动 12345678910111213141516171819202122232425262728293031323334353637383940414243444546<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>Title</title> <style> * { margin: 0; padding: 0; } .clearfix::after, .clearfix::before { content: \"\"; display: table; } .clearfix::after { clear: both; } .left { width: 20%; float: left; background: red; } .right { margin-left: 20%; background: blue; } .footer { border: 1px solid red; } </style></head><body> <main class=\"container clearfix\"> <section class=\"left\"> <p>lzx</p> </section> <section class=\"right\"> <p>hello</p> <p>world</p> </section> </main> <footer class=\"footer\">test</footer></body></html> 2.使用flex 1234567891011121314151617181920212223242526272829303132333435363738394041<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>Title</title> <style> * { margin: 0; padding: 0; } .container { display: flex; } .left { width: 20%; float: left; background: red; } .right { flex: 1; background: blue; } .footer { border: 1px solid red; } </style></head><body><main class=\"container\"> <section class=\"left\"> <p>lzx</p> </section> <section class=\"right\"> <p>hello</p> <p>world</p> </section></main><footer class=\"footer\">test</footer></body></html> 跑一下上述两段代码。会发现flex的方式是两栏等高布局,而float的这种不是。 左中右布局 1.浮动 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>Title</title> <style> * { margin: 0; padding: 0; } .clearfix::after, .clearfix::before { content: \"\"; display: table; } .clearfix::after { clear: both; } .left { width: 20%; float: left; background: red; } .center { margin: 0 20%; background: deeppink; } .right { width: 20%; float: right; background: blue; } .footer { border: 1px solid red; } </style></head><body><main class=\"container clearfix\"> <section class=\"left\"> <p>lzx</p> </section> <article class=\"center\"> <p>I love you, looking for my be'shirt</p> </article> <section class=\"right\"> <p>hello</p> <p>world</p> </section></main><footer class=\"footer\">test</footer></body></html> 这时候会发现一个问题,蓝色的会在下面,解决方式是将蓝色节点放在粉红节点的前面。引申原理会到BFC,会再开一篇博客介绍。 2.flex 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>Title</title> <style> * { margin: 0; padding: 0; } .container { display: flex; } .left { width: 20%; float: left; background: red; } .center { flex: 1; background: deeppink; } .right { width: 20%; background: blue; } .footer { border: 1px solid red; } </style></head><body><main class=\"container\"> <section class=\"left\"> <p>lzx</p> </section> <section class=\"center\"> <p>I love you, looking for my be'shirt</p> </section> <section class=\"right\"> <p>hello</p> <p>world</p> </section></main><footer class=\"footer\">test</footer></body></html> 水平居中 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>Title</title> <style> * { margin: 0; padding: 0; } .tc { text-align: center; } .draw { width: 30px; height: 30px; } .container { position: relative; padding-bottom: 30px; } .plc { position: absolute; left: 0; right: 0; margin: auto; width: 120px; text-align: center; } .plc2 { position: absolute; left: 50%; transform: translate3d(-50%, 0, 0); } .fcc { display: flex; justify-content: center; } .fighting { width: 120px; margin: 0 auto; } </style></head><body> <!-- inline inline-block --> <article class=\"tc\"> <span>center</span> </article> <div class=\"tc\"> <img class=\"draw\" src=\"./yourjobthecentralpark.png\" alt=\"\"> </div> <!-- position --> <div class=\"container\"> <p class=\"plc\">我是水平的中心</p> </div> <div class=\"container\"> <p class=\"plc2\">我是水平的中心</p> </div> <!-- flex --> <article class=\"fcc\"> <p>你们好,我是篮球少年,小林同学</p> </article> <!-- margin --> <p class=\"fighting tc\">加油,小林</p></body></html> 垂直居中 1234567891011121314151617181920212223242526272829303132333435363738394041424344<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>Title</title> <style> * { margin: 0; padding: 0; } .lh { height: 30px; line-height: 30px; border: 1px solid red; } .fac { display: flex; align-items: center; height: 30px; border: 1px solid blue; } .container { position: relative; height: 30px; border: 1px solid deeppink; } .ptc { position: absolute; top: 50%; transform: translate3d(0, -50%, 0); } </style></head><body> <!-- text line-height --> <p class=\"lh\">没事看看后端,说不定以后会用上</p> <!-- flex --> <p class=\"fac\">php是最好的语言,鸟哥是最强的php程序员</p> <!-- position --> <article class=\"container\"> <p class=\"ptc\">java收费,未来难说</p> </article></body></html> 代码仓库:https://github.com/Choicelin/css_structure","categories":[],"tags":[{"name":"CSS","slug":"CSS","permalink":"https://choicelin.github.io/tags/CSS/"}]},{"title":"升级React:Redux","slug":"升级React-Redux","date":"2018-11-17T02:33:59.000Z","updated":"2018-11-17T10:34:18.000Z","comments":true,"path":"2018/11/17/升级React-Redux/","link":"","permalink":"https://choicelin.github.io/2018/11/17/升级React-Redux/","excerpt":"","text":"本文翻译于原文链接 Redux是一个在javascript应用中同时管理数据和UI视图状态的工具。它被认为是完美管理单页应用复杂状态的杰作。同时它不是一定要用于一个特定的框架上(比如React),虽然它的作者心中是想解决React的复杂状态管理,但是它依然可以用在Angular,甚至jQuery应用之上。 告诉大家更多的是,它是经过一项“time travel”的实验而想出来的——这是它被开发出来的真实的原因,我们将在后面讨论。 就像我们之前的教程中看到的一样,React在组件间流动数据,更具体的说,这被叫做单向数据流——数据流向是从父到子一个方向的。对于这个单向数据流特征,React没有明显表示如何处理两个没有父子关系的组件的通信: React不推荐直接像上图那样组件与组件间通信。即使它特性支持上面的通信,也不要这样做。因为这种实践被认为是不好,极易导致代码写得像面条一样混乱。 React确实提供了一种解决上述问题的方案,但是它并没有在框架层面实现,而是需要开发者,也就是你,自己去实现它。下面是一段React文档的原文: For communication between two components that don’t have a parent-child relationship, you can set up your own global event system. … Flux pattern is one of the possible ways to arrange this. 翻译为:对于无父子关系的两个组件间的通信,你可以搭建你自己的全局事件系统。…Flux模式是可能处理这个问题的多种方式之一。 这就是Redux出来的原因。Redux提供了一种方案把你所有的应用状态保存在一个地方,叫做”store“。然后组件把状态的变化通知到store,同时组件订阅store,就可以察觉到状态的变化,从而不直接在组件间通信。 store在整个应用中可以看做所有状态变化的中间人。引入Redux,组件之间不直接交流,而是遵循”single source of truth“(译:单真理之源),也就是store这个真理之源。这和其他方式中,应用的各部分直接互相交流有很大不同。有时,那些方式被认为是错误的或者令人混淆的: 引入redux,很清晰地知道组件获取它们的状态通过唯一的store。同时也清晰地知道,组件在何处通知状态的改变,也是store。一个组件的通知状态改变只聚焦在dispatch(译:派遣)一个改变到store上,而不需要关心其他的组件是否需要这个状态的变化。所以Redux使数据流容易去推测。 使用store去协调一个应用的状态实际上是一种模式,也就是上文提到的Flux 模式。它是一个设计模式赞扬了像React那样的单向数据流的工程。Redux与Flux相似,但是它们怎样相似呢? Redux is “Flux-like”(译:Redux是”像Flux的“)Flux是一种模式,而不是像Redux是个工具,因此不是一个你可以下载的东西。尽管Redux是一个在其它东西像Elm,被Flux影响的工具。有很多指导文章在那里去比较Redux和Flux。它们大多数总结Redux是Flux或者像Flux,取决于一个人定义Flux规则有多严格。最终,这都不重要。Facebook非常喜欢和支持Redux,以至于最后聘请了Redux的核心开发者Dan Abramov。 这篇文章假设你一点也不熟悉Flux模式。但是如果你很熟悉,你会发现一些小的不同,特别是考虑Redux的三大指导法则: Single source of truth(译者注:单真理之源) Redux对于整个应用的状态仅仅使用一个store。因为所有的状态放在一个地方,Redux把这个叫做Single source of truth。 store的数据结构最终取决于你,但是它对于一个真实的应用,是一个典型的深度嵌套的对象。 Redux一个store的方式是与多个store的Flux众多的不同之一。 状态(state)是只读的 根据Redux文档,”改变状态的唯一方式是发出一个action,一个描述发生了什么的对象。“ 这意味着这个应用不能直接改变状态,而是通过action来表达一种去改变store里面的状态的意图。 store对象本身只有一个非常小的API对应4个方法: store.dispatch(action) store.subscribe(listener) store.getState() replaceReducer(nextReducer) 因此你可以看到,没有一个方法是设置状态的。因此,对于一个应用来说,发出一个action是唯一可以反映一个状态变化的方式: 1234567var action = { type: 'ADD_USER', user: {name: 'Dan'}};// 假设一个store对象已经被创建了store.dispatch(action); dispatch()方法发送一个对象到Redux,这个对象被我们称为action。这个action被描述为一个”payload(译:载荷,承载着状态修改时要用到的信息)“持有一个type和其他修改状态时要用到的数据——这个例子里面是一个user对象。记住,除了type这个属性,其他的载荷数据就看你如何去设计,不是固定的,比如这里使用一个user对象,用在reducer中。 修改由纯函数组成 就像刚才描述的那样,Redux不允许应用直接修改状态,而是用传递的action表明状态的变化,并意图去修改状态。Reducers是一些函数可以接收发送过来的action,并真正的去操作状态的修改。 一个reducer接收当前的状态作为一个参数,并能够通过构造出一个新状态来修改原来状态: 12345// Reducer 函数var someReducer = function(state, action) { ... return state;} Reducer应该使用纯函数来编写。纯函数描述了一个函数具有下述四个特点: 它不调用外部网络和数据库 它的返回值仅仅依赖于它的参数 它的参数应该是不能改变的 用同样的实参调用一个纯函数将总是返回相同的结果 这些被称作”纯“,因为它什么也不做但是返回的值基于它的参数。它对系统其它部分没有任何副作用。 我们的第一个Redux Store首先,用Redux.createStore()创建一个store并且传递所有的reducer作为它的参数。让我们看看一个仅有一个reducer的简单例子: 12345678910111213141516171819202122// 注意使用.push()不是一个最好的方式。这里只是为了简单的示例。// 我们将在后面的章节讲述为什么// reducer函数var userReducer = function(state, action) { if (state === undefined) { state = []; } if (action.type === 'ADD_USER') { state.push(action.user); } return state;}// 通过传入一个reducer创建一个storevar store = Redux.createStore(userReducer);// 传递我们第一个action来传达一个改变状态的意图store.dispatch({ type: 'ADD_USER', user: {name: 'Dan'}}) 简单的总结一下上面发生了什么: store通过一个reducer创建; reducer创建了这个应用的初始状态为一个空的数组; store.dispatch()用一个新的user对象派遣了action reducer将新的user对象加入到状态里并且返回了它,这个返回的过程更新了store reducer实际上被调用了两次在这个例子里面——一次是当store创建的时候,而另一次则是派遣action的时候。 当store被创建,Redux立即调用了reducers并且使用了它们返回的值作为初始的状态。第一次调用reducer时state是undefined。reducer的代码预期到这种情况,于是返回了一个空的数组来作用store的初始状态。 Reducers也在actions被派遣的时候被调用。因为从reducer返回的状态总是变成一个store的新的状态,所以Redux总是期望reducers返回一个状态。 在这个例子中,对我们reducer的第二个调用来自于我们的对action的派遣(译者注:store.dispatch())。记住,一个派遣的action描述了修改状态的意图,并且经常每次都承载着数据用于新状态。我们这次,Redux传递一个当前的状态(译者注:state)和一个action对象给reducer函数。这个action对象,现在有一个type属性叫做”ADD_USER“,允许了这个reducer来知道如何去改变状态。 (未完待续)","categories":[],"tags":[{"name":"react","slug":"react","permalink":"https://choicelin.github.io/tags/react/"}]},{"title":"NODE_ENV作用知多少","slug":"NODE-ENV作用知多少","date":"2018-11-08T08:13:58.000Z","updated":"2018-11-08T08:20:40.000Z","comments":true,"path":"2018/11/08/NODE-ENV作用知多少/","link":"","permalink":"https://choicelin.github.io/2018/11/08/NODE-ENV作用知多少/","excerpt":"","text":"场景我们在开发的时候经常会有生产环境和开发环境,这两种环境中可能用到不同的配置,比如本地域名和线上域名,如何去区别它们,是我今天要谈到的一个问题。 process对象与NODE_ENVNode.js中有一个全局的对象process,这个对象无需require(),它控制着Node.js的进程的信息。 它有一个属性,叫做env。我下面将会使用这个属性,在它里面挂载一个NODE_ENV属性,我们通过这个NODE_ENV来判断开发环境和生产环境。 获取NODE_ENV由于我们有process对象,所以可以直接在任意代码处获取到NODE_ENV,并根据该对象的值判断环境,新建一个index.js,如下 123456789101112131415/** * Creator: JIZHUA<413807584@qq.com> * Date: 2018/11/8 * Time: 3:37 PM */let url = ''if (process.env.NODE_ENV == 'dev') { url = `http://localhost:3000`} else { url = `http://www.fenxiangtech.com`}console.log(url) 设置NODE_ENV我们既然能够获取到NODE_ENV,那么我们如何设置这个NODE_ENV。即我们要在生产环境设置NODE_ENV值为production,在开发环境设置NODE_ENV的值为dev。 首先开起你的命令行,在windows下是CMD,在mac下是terminal。 windows下: 1set NODE_ENV=dev node index.js mac下: 1export NODE_ENV=dev node index.js 现在在当前窗口下,输出的url就是http://localhost:3000,关闭窗口后需要重新设置 在webstorm中设置NODE_ENV 点击右上角的edit configuration 找到environment variables 设置NODE_ENV 使用cross-env实现跨平台设置NODE_ENV首先初始化一个package.json 1npm init -y 然后安装cross-env 1npm i cross-env -D 接着在package.json的scripts中加入下面的代码 123456789101112131415{ \"name\": \"node-test\", \"version\": \"1.0.0\", \"description\": \"\", \"main\": \"index.js\", \"scripts\": { \"test\": \"cross-env NODE_ENV=dev node index.js\" }, \"author\": \"\", \"license\": \"ISC\", \"dependencies\": {}, \"devDependencies\": { \"cross-env\": \"^5.2.0\" }} 接着在命令行中运行npm run test既可看到结果 总结这就是NODE_ENV的作用,这个变量是自定义的名字,但是env这个属性不是哦。大家了解了吗?欢迎指正,交流!","categories":[],"tags":[{"name":"node","slug":"node","permalink":"https://choicelin.github.io/tags/node/"}]},{"title":"ESLIN配置airbnb标准","slug":"ESLIN配置airbnb标准","date":"2018-08-25T01:20:18.000Z","updated":"2018-08-25T01:36:32.000Z","comments":true,"path":"2018/08/25/ESLIN配置airbnb标准/","link":"","permalink":"https://choicelin.github.io/2018/08/25/ESLIN配置airbnb标准/","excerpt":"","text":"安装依赖1234( export PKG=eslint-config-airbnb; npm info "$PKG@latest" peerDependencies --json | command sed 's/[\\{\\},]//g ; s/: /@/g' | xargs npm install --save-dev "$PKG@latest") (linux || mac osx) 命令行操作1eslint --init 选use popular style guide ->接着选airbnb->根据需要选择是否要react->config格式选json 这样会创建一个.eslintrc.json IDE举例 webstorm中Preference中搜索eslint然后选择automatic search,这样ide会自动寻找eslintrc 你会发现格式化的时候与eslint的配置的检查相冲突,那么需要单独配置,具体方法是根据格式化以后的报错信息在Code Style -> javascript中修改格式化方式 总结最可操作,最简单的方式就是这样,欢迎尝试","categories":[],"tags":[{"name":"笔记","slug":"笔记","permalink":"https://choicelin.github.io/tags/笔记/"}]},{"title":"css积累一","slug":"css积累一","date":"2018-08-24T11:03:59.000Z","updated":"2018-08-24T11:13:12.000Z","comments":true,"path":"2018/08/24/css积累一/","link":"","permalink":"https://choicelin.github.io/2018/08/24/css积累一/","excerpt":"","text":"span,text之间的换行会导致空格的出现,引出一些样式bug border写了下方要在上方补一个透明的,同适用于padding,为了垂直居中,水平方向也是 div高度由其内部文档流元素的高度总和决定 文档流是文档内元素的流动方向 内联元素从左往右遇到宽度不够换行,英文是以单词为整体换行如果要字母截断使用word-break: break-all;否则是break-word 块级元素每一个块占一行,从上往下 span高度与字体的各种因素决定,无法确定 字体font-size大小指字最高到最低处的高度,每个字体都有建议行高,是由设计师确定的 遇到div中有内联元素要确定高度必须写height","categories":[],"tags":[{"name":"css","slug":"css","permalink":"https://choicelin.github.io/tags/css/"}]},{"title":"微信小程序一次性事件实现","slug":"微信小程序一次性事件实现","date":"2018-08-24T08:14:56.000Z","updated":"2018-08-24T08:17:46.000Z","comments":true,"path":"2018/08/24/微信小程序一次性事件实现/","link":"","permalink":"https://choicelin.github.io/2018/08/24/微信小程序一次性事件实现/","excerpt":"","text":"场景在开发原生小程序时,可能会一种场景,比如我对某一个点击事件,多次点击只触发一次,这是常见的once函数做的事情,但是如何用在小程序里面呢,下面给大家看看我的处理方法。 once函数123456789101112131415function once(fn, context) { return function() { if (fn) { fn.apply(context || this, arguments) fn = null } }} 小程序代码示例 wxml 1<view bindtap=\"click\">点我</view> js 12345678910111213141516171819202122232425262728293031323334353637383940414243Page({ data: {}, result: null, onLoad: function () { this.result = once(this.actualClick, this) }, actualClick() { console.log(1) }, click() { this.result() },})function once(fn, context) { return function() { if (fn) { fn.apply(context || this, arguments) fn = null } }} 总结once返回的函数要存起来,而且只存一次,并且要获取到page实例,所以需要在onload里面初始化。","categories":[],"tags":[{"name":"记录","slug":"记录","permalink":"https://choicelin.github.io/tags/记录/"}]},{"title":"谈谈引用和求值策略","slug":"谈谈引用和求值策略","date":"2018-07-23T02:32:25.000Z","updated":"2018-08-05T11:35:32.000Z","comments":true,"path":"2018/07/23/谈谈引用和求值策略/","link":"","permalink":"https://choicelin.github.io/2018/07/23/谈谈引用和求值策略/","excerpt":"","text":"说说引用值搞清楚引用之前先看看值是什么。在计算机科学中,一个值代表了一个程序可操作控制的实体。某种数据类型的一些成员就是这种类型的一些值。 指针在计算机科学中,指针是一个对象,它存储了另一个在内存中值的内存地址。一个指针引用了内存中的一个位置。 地址在计算机中,一个内存地址是一个对一块特定内存位置的引用。 引用在计算机科学中,引用是一个值,它能够让程序在内存或其他设备存储中间接获取到特定的数据,比如变量的值或者一个记录。对一个给定系统中存储在内存中的引用,它的实现是数据存储在内存或其他设备存储空间中的物理地址。但是正是由于刚才这种说法导致了大家对指针和地址的错误的混淆,并说一个引用是指向了这个数据。然而,引用的可能以其他的方式来实现。 小结引用不是指针,因为它可能实现方式是保存了一个物理地址,也可能是别的方式,比存的一个相对的地址偏移,而指针则是保存一个物理地址,显得更加狭义一点。同时,引用是一个值。 说说求值策略求值策略是程序语言决定什么时候来求一个函数调用的实参的值,同时计算什么类型的值来传递给一个函数。 通过值调用通过值调用也被说是传递值。在这种方式中,实参表达式被计算的结果会绑定到调用后相匹配的变量上(对应于形参的位置),并且将这个值复制后放到一个新的内存区域。这种方式的特点是,函数作用域外的被传递的变量是不变的。 通过引用调用通过引用调用也被说是传递引用。在这种方式中,函数接受一个变量的隐式引用作为实参,而不是它的值的拷贝。这种方式的特点是,函数作用域外被传递的变量也会被改变。 通过共享调用通过共享调用也被说是通过对象或通过对象共享调用,Java,Python,JavaScript中都存在这中求值策略。关键在于传递的是一个对象,这个对象并没有被复制放到一个新的内存区域,而是在调用者和被调用者的作用域中共享着。和通过值调用的不同是,这个对象在调用者作用域中被修改了。和通过引用调用的不同是,获取值的权限并不在调用者,因为传递的不是一个引用,而是一个对象。","categories":[],"tags":[{"name":"计算机科学","slug":"计算机科学","permalink":"https://choicelin.github.io/tags/计算机科学/"}]},{"title":"在vue中配置使用SCSS预编译","slug":"在vue中配置使用SCSS预编译","date":"2018-07-18T11:12:15.000Z","updated":"2018-08-05T11:35:32.000Z","comments":true,"path":"2018/07/18/在vue中配置使用SCSS预编译/","link":"","permalink":"https://choicelin.github.io/2018/07/18/在vue中配置使用SCSS预编译/","excerpt":"","text":"前言 vue-cli 使用webpack模板生成的项目需要引入scss预处理器,网上散人教程不够官方,这里参照官方文档写一个简化步骤 步骤 1234# use npmnpm install sass-loader node-sass --save-dev# use yarnyarn add -D sass-loader node-sass 1234567891011// bulid文件夹下的webpack base配置中的module的rules数组加入以下对象{ test: /\\.vue$/, loader: 'vue-loader', options: { loaders: { scss: 'vue-style-loader!css-loader!sass-loader', // <style lang=\"scss\"> sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax' // <style lang=\"sass\"> } }} style中lang改为scss即可 123<style lang="scss"></style> 需要的同学可以收藏 (逃)","categories":[],"tags":[{"name":"vue","slug":"vue","permalink":"https://choicelin.github.io/tags/vue/"}]},{"title":"在mpvue中使用图表插件wx-charts","slug":"在mpvue中使用图表插件wx-charts","date":"2018-07-06T02:00:43.000Z","updated":"2018-08-05T11:35:32.000Z","comments":true,"path":"2018/07/06/在mpvue中使用图表插件wx-charts/","link":"","permalink":"https://choicelin.github.io/2018/07/06/在mpvue中使用图表插件wx-charts/","excerpt":"","text":"前言 大家在原生小程序中需要用到图表插件时,可能会选择echarts或者由xiaolin3303开发的一款wx-charts插件,本文为大家剖析一下如何在开源小程序框架mpvue中使用wx-charts插件 原理 mpvue编译后的代码是原生的小程序代码,它把页面的wxml内容全部封成了template引入到page中。既然是最后会转为原生代码,就支持使用原生的canvas组件,这一点也是官方明确肯定的。所以在mpvue中使用wx-charts就跟在原生中使用一样,除了注意事件的绑定使用mpvue的方式即可。 步骤1. 1git clone git@github.com:xiaolin3303/wx-charts.git 在clone下来的dist文件夹获得wxcharts-min.js(混淆版本),如果需要进行二次开发,可以使用wxcharts.js然后修改 内容,达到定制化的目的,修改完后可以利用在线混淆进行重新混淆使用。 12<!--/src/pages/bar/bar.vue template中内容 --><canvas canvas-id=\"areaCanvas\" class=\"canvas\" @touchstart=\"touchHandler\"></canvas> 3.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859// /src/pages/bar/bar.vue script中内容var wxCharts = require('@/lib/wxcharts-min.js'); //该路径为该js文件在你项目中的路径export default { name: \"bar\", data() { return { bar: null } }, mounted: function (e) { var windowWidth = 320; try { var res = wx.getSystemInfoSync(); windowWidth = res.windowWidth; } catch (e) { console.error('getSystemInfoSync failed!'); } this.bar = new wxCharts({ canvasId: 'areaCanvas', type: 'area', categories: ['1', '2', '3', '4', '5', '6'], animation: true, series: [{ name: '成交量1', data: [32, 45, null, 56, 33, 34], format: function (val) { return val.toFixed(2) + '万'; } }], yAxis: { title: '成交金额 (万元)', format: function (val) { return val.toFixed(2); }, min: 0, fontColor: '#8085e9', gridColor: '#8085e9', titleFontColor: '#f7a35c' }, xAxis: { fontColor: '#7cb5ec', gridColor: '#7cb5ec' }, extra: { legendTextColor: '#cb2431' }, width: windowWidth, height: 200 }); }, methods: { touchHandler: function (e) { console.log(this.bar.getCurrentDataIndex(e)); this.bar.showToolTip(e); } } } 总结 该图表支持tooltip,这个功能比较实用,目前mpvue-echarts是不支持这一点的,但是echarts会更加美观,可以根据需求取舍","categories":[],"tags":[{"name":"心得体会","slug":"心得体会","permalink":"https://choicelin.github.io/tags/心得体会/"}]},{"title":"异步流程控制的两种思路","slug":"异步流程控制的两种思路","date":"2018-05-15T15:13:49.000Z","updated":"2018-05-15T15:14:34.000Z","comments":true,"path":"2018/05/15/异步流程控制的两种思路/","link":"","permalink":"https://choicelin.github.io/2018/05/15/异步流程控制的两种思路/","excerpt":"","text":"异步流程控制的两种思路 第一种在异步回调里面执行操作,这是最为常见的,扩展一下可以在回调参数里面再传回调,然后这个传入的回调里面执行可以保证异步逻辑走完了 思路二,我称之为赛跑回调,跟第一种有点类似,但是有细微的区别,有一个赛跑的感觉,也是在异步回调里面注册一个回调,把数据放到回调形参,这个回调可以在其他地方调用,在其他地方使用时,判断一下数据是否到位,如果到位了执行你的逻辑,如果没到位,执行回调,接受实参,设置数据,然后再执行你的逻辑。这就好比如果接口跑的快就不用调回调,跑得慢就调,所以好像赛跑一样。","categories":[],"tags":[{"name":"ajax","slug":"ajax","permalink":"https://choicelin.github.io/tags/ajax/"}]},{"title":"node写接口无响应原因总结","slug":"node写接口无响应原因总结","date":"2018-05-15T15:12:38.000Z","updated":"2018-05-15T15:13:12.000Z","comments":true,"path":"2018/05/15/node写接口无响应原因总结/","link":"","permalink":"https://choicelin.github.io/2018/05/15/node写接口无响应原因总结/","excerpt":"","text":"可能原因 路由路径不对 未调用next 中间件问题 debug方法 先注释全部路由以外的中间件,然后再依次打开,看是哪个中间件出问题","categories":[],"tags":[{"name":"node","slug":"node","permalink":"https://choicelin.github.io/tags/node/"}]},{"title":"前端工作积累的知识点二","slug":"前端工作积累的知识点二","date":"2018-05-15T15:10:19.000Z","updated":"2018-07-01T09:33:30.000Z","comments":true,"path":"2018/05/15/前端工作积累的知识点二/","link":"","permalink":"https://choicelin.github.io/2018/05/15/前端工作积累的知识点二/","excerpt":"","text":"picker、scroll-view上使用flex是无效的,需要在内部嵌套一层view再使用flex效果 filter(), concat() 和 slice()均不会修改原数组而是返回一个新数组 php var_dump()查看表达式的值和类型,gettype()类似js typeof, is_type()判断是否是type类型 php 布尔字面量是大写,(bool)可以强转,但是boolean才是类型字符 falsy值: FALSE,0,0.0,””, “0”, [], NULL尚未赋值变量还有一个空标记生成的Si没票了XML对象 php round()四舍五入,int()截断,php没有整除运算符 永远不要比较两个浮点数是否相等,如果硬要比较,可以使用机器极小值(epsilon)或最小单元取整数, 是计算中所能接受的最小的差别值 NAN用is_nan比较检查 用超出字符串长度的下标写入将会拉长该字符串并以空格填充,用负数下标读取字符串时返回空字符串, 写入时只用到了赋值字符串的第一个字符。用空字符串赋值则赋给的值是 NULL 字符 strval()可以转换为字符串,TRUE会被转为”1”,FALSE会被转为”” 小程序app.js中所有接口获取到的内容都可以在小程序全部页面共享,app.global,切记 open页面是为了在A小程序中分享这个open页面,然后根据options,拼参数跳转到B小程序 koa2 ctx.type可以设置Content-Type getter 后面跟的类似function的东西,其实名字是一个对象的属性,在用取值的时候执行里面的逻辑 最佳实践,在初始化生命周期中,拿数据及其配置操作时,最好加loading,在所有数据到位以后再做处理,这也是对异步流程的一种控制,所以说异步走到最后还是类似于同步的处理,也就是异步流程控制 query需要encodeURIComponent的时候不要把整个query进行encode,而是需要排除=,&这种符号,否则会出问题 做手持弹幕,旋转90度时把宽为100vh高为100vw时,变形,是因为旋转中心没有居中,先把div居中(通过定位居中,flex居中会有问题),再旋转,注意,不是调旋转中心,而是让要旋转的div居中 做定位居中和动画同时使用不同transform时,需要在动画里重复写 设置picker-view value需要setTimeout,但是此时会触发bindchange事件,所以 1234this.pickChangeFlag = falsesetTimeout(() => { this.pickChangeFlag = true}, 200) 图片加载可以加loading效果,未加载出来的时候,加上background-image给一个loading图片","categories":[],"tags":[{"name":"前端","slug":"前端","permalink":"https://choicelin.github.io/tags/前端/"}]},{"title":"bug心路2","slug":"bug心路2","date":"2018-05-03T16:55:23.000Z","updated":"2018-05-03T17:00:24.000Z","comments":true,"path":"2018/05/04/bug心路2/","link":"","permalink":"https://choicelin.github.io/2018/05/04/bug心路2/","excerpt":"","text":"今天做一个node项目,遇到一个bug,刚开始没明显报错,定位都没法定位。 至于原因是用了别人的脚手架不太熟悉,这个脚手架把错误处理都搞得较为复杂。 后来搞了很久,把脚手架里面所有的错误处理全部替换为最简单的,然后报错就来了瞬间就解决了。 关于解决问题,如果想要解决就能够解决,现在越来越能够沉着的有耐心的去解决一个问题,对于复杂的东西就简单化,记得当年听一个大哥跟一个小弟,找一个最简单的示例,这个不够简单,再找一个。","categories":[],"tags":[{"name":"心得体会","slug":"心得体会","permalink":"https://choicelin.github.io/tags/心得体会/"}]},{"title":"mysql学习笔记","slug":"mysql学习笔记","date":"2018-04-06T00:17:08.000Z","updated":"2018-04-17T02:46:18.000Z","comments":true,"path":"2018/04/06/mysql学习笔记/","link":"","permalink":"https://choicelin.github.io/2018/04/06/mysql学习笔记/","excerpt":"","text":"mysql命令行mysql登录 查看版本 1mysql -V root权限登录 p密码 P端口 h主机地址 回车后输入密码或者-p后跟密码 1mysql -uroot -p -P3306 -h127.0.0.1 登录后退出123exit;quit;\\q; 修改提示符 \\D完整日期 \\d当前数据库 \\h服务器名称\\u当前用户 可以互相组合 1mysql -uroot -proot --prompt \\h 登录以后 1prompt mysql> 常用命令 查版本、显示当前日期、显示当前用户 123SELECT VERSION();SELECT NOW();SELECT USER(); 规范 关键字、函数名称全部大写 数据库名称、表名称、字段名称全部小写 SQL语句必须以分号结尾 创建数据库示例 最简单的版本 1CREATE DATABASE t1; 在未存在该数据库时才创建 1CREATE DATABASE IF NOT EXISTS t1; 此时数据库存在,所以可以用SHOW WARNINGS;查看警告信息,SHOW CREATE DATABASE t1;可以查看创建t1的命令 再加入编码方式 1CREATE DATABASE IF NOT EXIST t2 CHARACTER SET gbk; 如果想修改编码方式则参考下面的内容 查看当前服务器下的数据列表 基本示例 1SHOW DATABASES; 修改数据库示例 修改除了没有IF NOT EXISTS 和 CREATE改为ALTER其他部分都一样,下面修改字符集 1ALTER DATABASE t1 CHARACTER SET utf8; 删除数据库示例 删除 1DROP DATABASE IF EXISTS t1; MySQL数据类型整型 TINYINT(-128-127,0-255) 1字节 SMALLINT(-32768-32767,0-65535) 2字节 MEDIUMINT(-8388608-8388607,0-4294967295) 3字节 INT(-2^31-2^31-1,0-2^32-1) 4字节 BIGINT(-2^63–2^63-1,0-2^64-1) 8字节 浮点型 FLOAT[(M,D)] M表示总位数D表示小数位数(小数最大7位) DOUBLE[(M,D)] 日期类型 YEAR 1 TIME 3 DATE 3 DATETIME 8 TIMESTAMP(时间戳) 4 字符型 CHAR(M) 0<=M<=255字节 定长类型,不足以空格补位 VARCHAR(M) L+1个字节,其中L<=M且0<=M<=65535 变长类型,有多长存多长 TINYTEXT L+1个字节,其中L<2^8 TEXT L+2个字节,其中L<2^16 MEDIUMTEXT 3 24 LONGTEXT 4 32 ENUM(‘value1’,’value2’,…) 1或2个字节,取决于枚举个数(最多65535个值) SET(‘value1’,’value2’,…) 1 2 3 4 8字节 取决于SET成员的数目最大64个成员 表USE 打开数据库(必须已经创建或已存在) 1USE t1; 创建数据表 创建一个存储用户信息的表 12345CREATE TABLE tb1( username VARCHAR(20), age TINYINT UNSIGNED, salary FLOAT(8,2) UNSIGHED); 查看数据表 查看数据表列表 1SHOW TABLES [FROM dbName(mysql)]; 查看数据表结构 查看表结构是查看列 1SHOW COLUMNS FROM tb1; 插入记录与查询记录 插入记录示例,如果省略列名称,需要全部赋值,还是以上面创建的用户信息表举例 1INSERT tb1 VALUES('Anger', 21, 7863.25); 插入记录示例,不省略列名称 1INSERT tb1(username, age) VALUES('Kira', 25); 简单查询示例,*匹配的是字段而不是记录,有人会说*是匹配所有记录,这是有点问题的 1SELECT * FROM tb1; 空值与非空 NULL 字段值可以为空,设置的时候可以省略 NOT NULL 字段值禁止为空 自动编号 自动编号,必须与主键结合使用 默认情况下,起始值为1,每次的增量为1 主键约束 一张数据表中只能存在一个主键 主键保证记录的唯一性 主键自动为NOT NULL 1234567CREATE TABLE tb3( id SMALLINT UNSIGHED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(20) NOT NULL);SHOW COLUMNS FROM tb3;INSERT tb3(username) VALUES('Kira');SELECT * FROM tb3; 唯一约束 唯一约束可以保证记录的唯一性 唯一约束的字段可以为空值(NULL) 每张数据表可以存在多个唯一约束 默认约束 当插入记录时,如果未明确为字段赋值,则会赋予默认值 12345678CREATE TABLE tb4( id SMALLINT NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(20) NOT NULL UNIQUE KEY, sex ENUM('1', '2', '3') DEFAULT '3');SHOW COLUMNS FROM tb4;INSERT tb6(username, sex) VALUES('Tom', 1);INSERT tb6(username) VALUES('Rose'); 再谈约束 约束保证数据的完整性和一致性 约束分为表级约束和列级约束,如果约束只针对某一个字段来约束,我们称之为列级约束,如果针对两个或两个以上字段来使用约束,则成为表级约束 约束类型包括(按功能分类) NOT NULL (非空约束) PRIMARY KEY(主键约束) UNIQUE KEY(唯一约束) DEFAULT(默认约束) FOREIGN KEY(外键约束) 外键约束 保持数据完整性和一致性 实现一对一或一对多的关系 外键约束要求 父表和子表必须使用相同的存储引擎,而且禁止使用临时表 数据表的存储引擎只能为InnoDB 外键列和参照列必须具有相似的数据类型。其中数字的长度或是是否有符号位必须相同,而字符长度则可以不同 外键列和参照列必须创建索引。如果外键列不存在索引的话,MySQL将会自动创建索引 PS : 子表是具有外键列的表,而子表所参照的表叫父表 外键列是加过FOREIGN关键字的列,而参照列则是外键列参照的列 编辑数据表的默认存储引擎 MySQL配置文件my.ini,修改完需重启MySQL服务 1default-storage-engine=INNODB 外键约束示例 省份表 12345CREATE TABLE provinces( id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, pname VARCAHR(20) NOT NULL);SHOW CREATE TABLE provinces; 用户表,pid参照省份表(父表)的id字段 123456CREATE TABLE users( id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, username VARCHAR(10) NOT NULL, pid BIGINT, FOREIGN KEY (pid) REFERENCES provinces (id) ); 上述操作会报错,因为父表的id与子表的pid数据类型不相似 修正后 123456CREATE TABLE users( id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, username VARCHAR(10) NOT NULL, pid SMALLINT UNSIGNED, FOREIGN KEY (pid) REFERENCES provinces(id) ); 外键约束的参照操作 这些操作是指在进行外键约束的情况下,在更新表的时候,子表是否也进行相应的操作 CASCADE 父表更新或删除行,也会更新或删除子表中匹配的行。而且插入数据时必须先在父表中插入,然后才能在子表中插入,原因是子表是参照父表的信息,如果没有父表信息,将导致子表无法参照 12345678910111213141516171819202122232425CREATE TABLE users1( id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL, pid SMALLINT UNSIGNED, FOREIGN KEY (pid) REFERENCES provinces (id) ON DELETE CASCADE);SHOW CREATE TABLE users1;INSERT provinces(pname) VALUES('A');INSERT provinces(pname) VALUES('B');INSERT provinces(pname) VALUES('C');SELECT * FROM provinces;INSERT users1(username, pid) VALUES('Tom', 3);INSERT users1(username, pid) VALUES('John', 1);INSERT users1(username, pid) VALUES('Rose', 3);SELECT * FROM users1;DELETE FROM provinces WHERE id = 3;SELECT * FROM provinces;SELECT * FROM users1; SET NULL 父表删除和更新行时,会设置子表中的外键列为NULL,但是必须在子表外键列没有设置NOT NULL的情况下 RESTRICT 拒绝对父表进行删除和更新操作 NO ACTION 与RESTRICT相同 在实际操作中,很少使用物理外键约束操作,而使用逻辑外键约束操作,原因是物理外键约束只有INNODB引擎才支持,逻辑外键就是我们在定义两张表的结构的时候,我们是按照存在着某种结构的方式去定义,但是不去使用FOREIGN KEY这个关键词来定义 表级约束与列级约束 如果按照功能划分会有主键约束,非空约束,唯一约束。而如果按照参照操作数目的多少来划分,则有表级和列级约束 列级约束 对一个列创建的约束,既可以在列定义时声明,也可以在列定义后声明 表级约束 对多个列创建的约束,只可以在列定义后声明 修改数据表 数据表的修改无非就是列的增加,列的删除,约束的添加,约束的删除等等一些操作 添加单列 12345SHOW COLUMNS FROM users1;ALTER TABLE users1 ADD age TINYINT UNSIGNED NOT NULL DEFAULT 10;ALTER TABLE users1 ADD password VARCHAR(32) NOT NULL AFTER username;ALTER TABLE users1 ADD realname VARCHAR(20) NOT NULL FIRST; 添加多列 与上面的语法相似,在ADD后面加上括号,不能指定位置关系(FIRST,AFTER) 删除列 12345ALTER TABLE users1 DROP realname;SHOW COLUMNS FROM users1;ALTER TABLE users1 DROP username, DROP age; 添加主键约束(默认B tree索引) 123456789CREATE TABLE users2( username VARCHAR(10) NOT NULL, pid SMALLINT UNSIGNED);SHOW CREATE TABLE users2;ALTER TABLE users2 ADD id SMALLINT UNSIGNED;ALTER TABLE users2 ADD CONSTRAINT PK_users2_id PRIMARY KEY (id); 添加唯一约束 1ALTER TABLE users2 ADD UNIQUE username; 添加外键约束 1ALTER TABLE users2 ADD FOREIGN KEY (pid) REFERENCES provinces (id); 添加或删除默认约束 123ALTER TABLE users2 ADD age SMALLINT UNSIGNED NOT NULL;ALTER TABLE users2 ALTER age SET DEFAULT 15;ALTER TABLE users2 ALTER age DROP DEFAULT; 删除主键约束 1ALTER TABLE users2 DROP PRIMARY KEY; 删除唯一约束 \\G指以网格方式呈现 12SHOW INDEXES FROM users2\\G;ALTER TABLE users2 DROP INDEX username; 删除外键约束 12SHOW CREATE TABLE users2;ALTER TABLE users2 DROP FOREIGN KEY users2_ibfk_1; 修改列定义 位置或列的类型有问题,是可以修改的,由大类型修改到小类型可能造成数据丢失 12ALTER TABLE users2 MODIFY id SMALLINT UNSIGNED NOT NULL FIRST;SHOW COLUMNS FROM users2; 修改列名称 既可以修改到列名称又可以修改到列定义 12ALTER TABLE users2 CHANGE pid p_id TINYINT UNSIGNED NOT NULL;SHOW COLUMNS FROM users2; 修改数据表名称123ALTER TABLE users2 RENAME users3;RENAME TABLE users3 to users2; 操作数据表中的记录插入记录1234567891011CREATE TABLE users( id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL, password VARCHAR(32) NOT NULL, age TINYINT UNSIGNED NOT NULL DEFAULT 10, sex BOOLEAN );INSERT users VALUES(NULL, 'Tom', '123456', 15, 1);INSERT users VALUES(DEFAULT, 'John', '321', 15, 1);INSERT users SET username='Ben', password='456'; 更新记录(单表更新)123UPDATE users SET age = age + 5;UPDATE users SET age = age - id, sex = 0; UPDATE users SET age = age + 10 WHERE id % 2 = 0; 删除记录(单表删除)1DELETE FROM users WHERE id = 6; 查询记录1234SELECT id, username FROM users;SELECT username, id FROM users;SELECT users.id, users.username FROM users;SELECT id AS userId, username AS uname FROM users; ( 未完待续)","categories":[],"tags":[{"name":"mysql","slug":"mysql","permalink":"https://choicelin.github.io/tags/mysql/"}]},{"title":"css可以继承的属性","slug":"css可以继承的属性","date":"2018-04-03T22:16:29.000Z","updated":"2018-04-03T22:43:32.000Z","comments":true,"path":"2018/04/04/css可以继承的属性/","link":"","permalink":"https://choicelin.github.io/2018/04/04/css可以继承的属性/","excerpt":"","text":"font-size text-indent text-align line-height word-spacing letter-spacing text-transform color visibility cursor direction font font-family font-style font-weight list-style ———————————————— 不常用 —————————————————– caption-side font-variant list-style-position list-style-type border-collapse border-spacing empty-cells table-layout list-style-type list-style-image quotes page page-break-inside widows orphans speak speak-punctuation speak-numeral speak-header speech-rate volume voice-family pitch pitch-range stress richness azimuth ","categories":[],"tags":[{"name":"css","slug":"css","permalink":"https://choicelin.github.io/tags/css/"}]},{"title":"Router进行代码分片","slug":"使用React和React Router进行代码分片","date":"2018-03-19T00:58:50.000Z","updated":"2018-03-19T12:02:40.000Z","comments":true,"path":"2018/03/19/使用React和React Router进行代码分片/","link":"","permalink":"https://choicelin.github.io/2018/03/19/使用React和React Router进行代码分片/","excerpt":"","text":"使用React和React Router进行代码分片 本文由林子翔翻译自原文链接 代码分片可以让你把应用分成多个包,使你的用户能逐步加载应用而变得流行起来。在这篇文章中,我们将会看一下什么是代码分片和怎么去做,了解怎么去配合React Router去实现它。 现在是2018年。你的用户不需要为了一小块内容而去下载整个应用。如果一个用户下载所有的代码,仅仅是为了请求一个注册页面是毫无意义的。而且用户在注册时并不需要下载用户设置页的巨大富文本编辑器代码。如果要下载那么多内容的话,是很浪费的。而且对于一些用户,他们会抱怨不尊重没有特别好带宽的他们。这个点子近年不仅很热,而且实现难度以指数级降低。甚至还有有了一个很酷的名字,代码分片。 这个点子很简单,即按需加载。实践的话,它可能有一点复杂。而复杂的原因并不是代码分片本身,而是现在有各种各样的工具来做这个事情。而且每个人对哪个方式最好都有自己的看法。当你第一次开始着手的时候,可能很困难分析什么是什么。 最常见的两种做法是使用Webpack和它的包加载器(bundle-loader),或者使用ECMAScript的stage3提案的动态import()。任何机会不用Webpack,我就不用,因此在这篇文章中,我将会使用动态import()。 如果你很熟悉ES模块,你应该知道它们是静态的。意思就是说你必须在编译时确定你要引入和导出的内容,而不是运行时。这也意味着你不能基于一些条件来动态导入一个模块。导入的内容必须声明在文件的最开头否则会抛出一个错误。 123if (!user) { import * as api from './api' //不能这样做,“import”和“export”只能出现在文件顶部} 现在,如果import不需要是静态的怎么办?意味着上面的代码可以工作?将会给我们带来什么好处?首先这意味着我可以按着需要加载某个模块。这非常强大,它让我们更接近按用户需要下载代码的想象。 12345if (editPost === true) { import * as edit from './editpost' edit.showEditor()} 假设editpost包含一个非常大的富文本编辑器,我们需要保证用户在没有使用它的时候不会去下载它。 另外一个很酷的例子用于遗留支持。你可以在浏览器确定确实没有的时候才下载对应代码。 好消息(我在上文中曾间接提及)。这种类型的方法确实存在,它被Create React App(React项目的一种官方创建方法)支持,而且它是ECMAScript stage3的提案。不同的是替换了你之前使用import的方式。它使用起来像一个方法,并返回一个Promise,一旦模块完全加载,就会把这个模块resolve回来。 12345if (editPost === true) { import('./editpost') .then(module => module.showEditor()) .catch(e =>)} 特别好,对吧? 现在我们知道怎么动态引入模块了,下一步是找出怎么结合React和React Router来使用它。 第一个(可能是最大的一个)问题,我们对React代码分片时,我们应该对哪里进行分片?典型的回答有两个 在路由的层次上分片 在组件的层次上分片 而更加常见的做法是在路由的层次上进行分片。你已经把你的应用分成了不同的路由,因此根据这个来代码分片是自然而然的事情。 让我以一个简单的React Router例子开始。我们将有三条路由分别是: /,/topics,/settings。 12345678910111213141516171819202122232425262728293031323334import React, { Component } from 'react'import { BrowserRouter as Router, Route, Link,} from 'react-router-dom'import Home from './Home'import Topics from './Topics'import Settings from './Settings'class App extends Component { render() { return ( <Router> <div> <ul> <li><Link to='/'>Home</Link></li> <li><Link to='/topics'>Topics</Link></li> <li><Link to='/settings'>Settings</Link></li> </ul> <hr /> <Route exact path='/' component={Home} /> <Route exact path='/topics' component={Topics} /> <Route exact path='/settings' component={Settings} /> </div> </Router> ) }}export default App 现在,假设我们的/settings路由内容非常多。它包含一个富文本编辑器,和一个原始超级马里奥兄弟的拷贝,和盖伊法利的高清图片。当用户不在/settings路由上时,我们不想让他们下载全部这些内容。让我们使用我们React和动态引入(import())的知识来分片/settings路由。 就像我们在React里解决任何问题一样,我们先写一个组件。我们将叫它DynamicImport。这个组件的目的是动态的加载一个模块,只要模块加载好了,就把它传给它子节点(children)。 1234567const Settings = (props) => ( <DynamicImport load={() => import('./Settings')}> {(Component) => Component === null ? <Loading /> : <Component {...props} />} </DynamicImport>) 上面的代码告诉我们两个重要的要素。第一,这个组件在执行时会接受一个属性load,将使用我们前面提到的语法动态引入一个模块。第二,这个组件会接受一个函数作为他的子节点,这个函数需要和引入进来的模块一起调用。 在我们深入思考DynamicImport的实现的之前,让我们想一下我们会怎么实现。第一件事我们需要确定的是要调用props.load。这让我们返回一个Promise,当它resolve的时候应该返回模块。接着,一旦我们有了模块,我们需要一种方式去触发重渲染,因此我们要把模块传给props.children并且调用它。怎样在React里面触发重渲染呢?设置state(setState)。通过把动态引入的模块加入到DynamicImport的state里面,就像我们之前使用的一样,我们遵循和React同样的过程- 获取数据 -> 设置到state里 -> 重渲染。而这一次我们只是把获取数据替换成了引入模块。 好了,首先,让我们加入初始的状态到组件里。 12345class DynamicImport extends Component { state = { component: null }} 现在,我们需要调props.load方法。这将返回一个promise同时在resolve后有一个模块 12345678910111213class DynamicImport extends Component { state = { component: null } componentWillMount () { this.props.load() .then(component => { this.setState(() =>{ component )} }) }} 这里有一个疑难杂症。如果我们ES模块和commonjs模块混用时,ES模块会有一个.default属性,而commonjs模块并没有。让我们改变一下代码,适应一下上面的情况。 12345678this.props.load() .then(component => { this.setState(() => { component: component.default ?component.default : component }) })}) 现在我们动态引入的模块并且把它加入到了state里面,最后一件事就是render方法长什么样了。如果你会记得,当DynamicImport使用的时候,它看起来像这样 1234567const Settings = (props) => ( <DynamicImport load={() => import('./Settings')}> {(Component) => Component === null ? <Loading/> : <Component {...props} />} </DynamicImport>) 注意我们给组件传了一个函数作为子节点。这意味着我们需要执行这个函数,传递的是这个引入在state里的组件。 123456789101112131415161718class DynamicImport extends Component { state = { component: null } componentWillMount () { this.props.load() .then((component) => { this.setState({ component: component.default ? component.default : component }) }) } render() { return this.props.children(this.state.component) }} 欧了,现在任何时候我们动态引入一个模块,我们可以把它包裹在DynamicImport。如果我们之前尝试用这种方法到我们路由上,我们的代码会看起来像这样 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566import React, { Component } from 'react'import { BrowserRouter as Router, Route, Link} from 'react-router-dom'class DynamicImport extends Component { state = { component: null } componentWillMount () { this.props.load() .then((component) => { this.setState({ component: component.default ? component.default : component }) }) } render() { return this.props.children(this.state.component) }}const Home = (props) => ( <DynamicImport load={() => import('./Home')}> {(Component) => Component === null ? <p>Loading</p> : <Component {...props} /> } </DynamicImport>)const Topics = (props) => ( <DynamicImport load={() => import('./Settings')}> {(Component) => Component === null ? <p>Loading</p> : <Component {...props}/> } </DynamicImport>)class App extends Component { render() { return ( <Router> <div> <ul> <li><Link to='/'>Home</Link></li> <li><Link to='/topics'>Topics</Link></li> <li><Link to='/settings'>Settings</Link></li> </ul> <hr /> <Route exact path='/' component={Home} /> <Route path='/topics' component={Topics} /> <Route path='/settings' component={Settings} /> </div> </Router> ) }}export default App 我们怎么知道这个确实起作用并且分片了我们的路由呢?如果你用一个React官方的Create React App创建一个应用跑一下npm run build,你将看到应用被分片了。 每一个包被一一引入进了我们的应用 你到了这一步,可以跳个舞轻松一下了 还记得我讲到有两种层级的代码分片方式吗?我们曾放在手边的引导 以路由层级分片 以组建层级分片 至此,我们只讲了路由层级的代码分片。到这里很多人就停止了。在路由层级上代码分片,就像刷牙一样,你天天刷,牙齿大部分很干净,但是还会有蛀牙。 除了思考用路由的分片方式,你应该想想怎么用组件的方式去分片。如果你在弹层里面有很多内容,路由分片还是会下载弹层的代码,无论这个弹层是否显示。 从这一点看,它更多是在你大脑里的一种变更而不是新知识。你已经知道如何使用动态引入,现在你需要找出哪些组件是在用到时才要下载的。 如果我不提React Loadable那我就是哑巴了。它是一个“通过动态引入加载组件的高阶组件”。重要的是,它处理所有我们提到的事情,并把它做成了一个精致的API。它甚至处理了很多很边角的事情,比如我们没有考虑服务端渲染和错误处理。看看它吧,如果你想要一个简单,开箱即用的解决方案的话。 (完,逃)","categories":[],"tags":[{"name":"翻译","slug":"翻译","permalink":"https://choicelin.github.io/tags/翻译/"}]},{"title":"wepy开发速查手册","slug":"wepy开发速查手册","date":"2018-03-11T08:25:24.000Z","updated":"2018-03-13T09:49:34.000Z","comments":true,"path":"2018/03/11/wepy开发速查手册/","link":"","permalink":"https://choicelin.github.io/2018/03/11/wepy开发速查手册/","excerpt":"","text":"事件bindtap=”click”替换为@tap=”click”,防止冒泡@tap.stop,防止捕获@tap.capture.stop,如果是@someEvent.user=”somefunction”,这种通过this.$emit(‘someEvent’),someFunction会触发而且写在父组件methods里面,同时父组件events里面的someEvent时不会执行 events示例 123456// events对象中所声明的函数为用于监听组件之间的通信与交互事件的事件处理函数 events = { 'someEvent': (p1, p2, p3, $event) => { console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`); } }; 事件传参变更,bindtap=”click” data-index=”“更改为@tap=”click()” repeat使用实例 12345<!-- 注意,使用for属性,而不是使用wx:for属性 --><repeat for=\"{{list}}\" key=\"index\" index=\"index\" item=\"item\"> <!-- 插入<script>脚本部分所声明的child组件,同时传入item --> <child :item=\"item\"></child></repeat> computed计算属性,只要是组件中有任何数据发生了改变,那么所有计算属性就都会被重新计算,代码中可通过this.计算属性名来引用,模板中也可通过来绑定数据 12345678910data = { a: 1}// 计算属性aPlus,在脚本中可通过this.aPlus来引用,在模板中可通过{{ aPlus }}来插值computed = { aPlus () { return this.a + 1 }} watcher监听器, 通过监听器watcher能够监听到任何属性的更新 1234567891011data = { num: 1}// 监听器函数名必须跟需要被监听的data对象中的属性num同名,// 其参数中的newValue为属性改变后的新值,oldValue为改变前的旧值watch = { num (newValue, oldValue) { console.log(`num value: ${oldValue} -> ${newValue}`) }} 数据绑定使用:号比如,不使用冒号是绑定字符串常量,sync不加,父组件这个变量变化就不会影响子组件,加了sync,同时,写了twoWay: true时就是双向绑定,即同一个属性(props)的变化,父修改了影响子,子修改了影响父 1<child :title=\"parentTitle\" :syncTitle.sync=\"parentTitle\" :twoWayTitle=\"parentTitle\"></child> 12345678910111213141516props = { // 静态传值 title: String, // 父向子单向动态传值 syncTitle: { type: String, default: 'null' }, //这里即twoWay twoWayTitle: { type: Number, default: 'nothing', twoWay: true }}; 通信 父组件发起的:$broadcast(特性:所有子组件都会收到此广播事件,顺序由上至下,广度优先) 子组件发起的:$emit(特性:事件发起组件的所有祖先组件会依次接收到事件,顺序由下至上) 组件间发起的:$invoke (特性:一个页面或组件对另一个组件中的方法的直接调用) $invoke示例(这里能否用到alias路径,大家可以试试) 1this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs'); 回调均在events对象里面写 wxs 123456789101112131415161718192021222324252627282930module.exports = { text: 'This is from wxs', filter: function (num) { return num.toFixed(2); }};// index.wpy<template> <text>{{m1.text}}</text> <text>{{m1.filter(num)}}</text></template><script> import wepy from 'wepy'; import mywxs from '../wxs/mywxs.wxs'; export default class Index extends wepy.page { data = { num: 10 }; wxs = { m1: mywxs } };</script> ","categories":[],"tags":[{"name":"wepy","slug":"wepy","permalink":"https://choicelin.github.io/tags/wepy/"}]},{"title":"微信小程序api之setEnableDebug说明","slug":"微信小程序api之setEnableDebug说明","date":"2018-03-04T07:20:01.000Z","updated":"2018-03-04T07:29:08.000Z","comments":true,"path":"2018/03/04/微信小程序api之setEnableDebug说明/","link":"","permalink":"https://choicelin.github.io/2018/03/04/微信小程序api之setEnableDebug说明/","excerpt":"","text":"使用方法1234567//app.jsonLaunch() { //开启调试 wx.setEnableDebug({ enableDebug: true })} 解释 这个接口相当于是用代码来在预览时开启调试,预览后打开小程序后会提示“重新打开小程序后生效”,点击确定后,在微信-发现-小程序中打开小程序,此时右下角会出现一个绿色可拖动的vConsole,可以显示调试信息 另一种非代码开启方式是点击小程序右上角的胶囊中三个点符号,在这里点击打开调试,会和上面讲的一样,出现同样的过程 如果设置enableDebug为false,会同样出现上述过程的逆过程,是一样的道理","categories":[],"tags":[{"name":"微信小程序","slug":"微信小程序","permalink":"https://choicelin.github.io/tags/微信小程序/"}]},{"title":"函数式编程入门","slug":"函数式编程入门","date":"2018-02-28T14:02:09.000Z","updated":"2018-03-04T05:24:42.000Z","comments":true,"path":"2018/02/28/函数式编程入门/","link":"","permalink":"https://choicelin.github.io/2018/02/28/函数式编程入门/","excerpt":"","text":"什么是函数式编程函数式编程,个人理解是在编写纯函数。 据印度Anto Aravinth的解释的函数式编程是一种范式,我们能依靠这种范式创建仅依赖输入就可以完成自身逻辑的函数。 而这种函数就是我所理解的纯函数。 纯函数特点 同样的输入,输出一定相同 函数不会改变任何外部环境的变量 函数式编程的引用透明性 即所有函数对于相同输入都返回相同的值,那么据此产生了一个术语,叫替换模型,即把一个函数的调用用它的返回值替换,这将带来可以缓存的代码,当函数调用以后,我们将它的返回值存在内存里(比如用一个对象的属性存起来),下次使用就直接取对象这个属性映射的值。 并发优势 在并发层面上,由于函数不会对外部环境变量进行修改,只依赖传入的参数,所以,并发执行两个或多个一样的函数时相互之间不会受到影响 抽象与高阶函数抽象 个人总结维基百科的表述,抽象即是一种管理复杂度的方法,把复杂度限定到你能处理的范围。这个范围层面的确定即是一种抽象。 比如高级程序语言就是一种抽象,它的底层是复杂的汇编语言以及机器语言,然而你把它抽象出来进行问题的处理,而不考虑底层复杂的调用和实现 抽象使我们关注预定的目标而不去考虑底层的细节实现 高阶函数 高阶函数就是接受函数作为参数并且/或者返回函数作为输出的函数 经常用到的比如map,forEach等 高阶函数的思维 高阶函数是一种声明式的抽象,声明式即研究“做什么”而不是“如何做”,而抽象使问题关注的地方更加单一,简单化 比如forEach函数抽象出了遍历数组这个过程,我们不用去关心如何去做遍历,而是专注于对每次遍历的结果的操作 柯里化一元函数与多元函数 只接受一个参数的函数叫一元函数,可以以此类推,几元函数就是接受几个参数的函数 变参函数 函数接受的参数不确定的函数叫做变参函数(比如使用arguments) 1234567/** * 变参函数 */function vary(...args) { console.log(args) //do something to args} 柯里化思想 所谓柯里化就是把多参函数转化为嵌套的单参函数 currying的一种实现1234567891011121314151617181920/** * 将多参函数转换为单参函数的一种实现 * @params fn 需要转化的多参函数 */function curry(fn) { //异常处理 if (typeof fn !== 'function') { throw new Error('fn is not a function') } //转换处理 return function curryInner(...args) => { //当传入函数的参数数目大于返回curryInner的参数长度时 if (fn.length > args.length){ return function() { return curryInner.apply(null, args.concat([].slice.call(arguments))) } } return fn.apply(null, args); }} 分析举例1234567891011121314151617181920212223/** * 测试函数作为实参传递 * @params x, y, z */function foo(x, y, z) { // do something}const curriedFn = curry(foo);/** * 此时curriedFn为 * function curryInner(...args) => { * if (fn.length > args.length){ * return function() { * return curryInner.apply(null, args.concat([].slice.call(arguments))) * } * } * return fn.apply(null, args) * } *///调用并传入实参1, 2 ,3curriedFn(1)(2)(3) 当执行到curriedFn(1)时,因为args.length为1,fn.length为3所以进入if块将args的参数连接,此时args.length为2 上一步返回的还是curryInner函数,再执行curriedFn(1)(2),此时由于args.length为2还是小于3,再重复上述步骤执行curriedFn(1)(2)(3) 此时args.length为3,if条件不满足,执行fn.apply(null, args),即相当于执行了foo(1,2,3) 文献参考:《JavaScript ES6 函数式编程入门经典》 【印】Anto Aravinth 著 梁宵 译","categories":[],"tags":[{"name":"心得体会","slug":"心得体会","permalink":"https://choicelin.github.io/tags/心得体会/"}]},{"title":"mark","slug":"mark","date":"2018-02-22T10:57:35.000Z","updated":"2018-02-22T11:04:42.000Z","comments":true,"path":"2018/02/22/mark/","link":"","permalink":"https://choicelin.github.io/2018/02/22/mark/","excerpt":"","text":"12345function test(a, b) {}function b(a, b) { test(a, b)} 1<SomeComponent callback={(a, b) => {this.test(a, b)}} /> 12345function test(a, b) {}function b(test) { test(a, b)} 1<SomeComponent callback={::this.test} />","categories":[],"tags":[{"name":"记录","slug":"记录","permalink":"https://choicelin.github.io/tags/记录/"}]},{"title":"html5的data-*","slug":"html5的data","date":"2018-02-12T10:51:30.000Z","updated":"2018-02-12T11:06:00.000Z","comments":true,"path":"2018/02/12/html5的data/","link":"","permalink":"https://choicelin.github.io/2018/02/12/html5的data/","excerpt":"","text":"引入看公司的表单组件,发现一个data-name属性,这个在最早做react的todo时,遇到过,因为获取不到直接定义的属性,还写过一篇肤浅的博客。其实这个并不是react里面的内容,而是html5里面的 data-*用法目前我没有参考文档,所以不知道它为什么会出现,但是我知道怎么用,在什么场景用,直接讲自己理解的用法。 获取这个属性,可以利用 1domNode.getAttribute('data-foo') 这个其中一个应用场景就是你在react中获取节点后,想获取这个data-属性里面的值 还可以在事件对象的当前对象下的dataset属性中获取 1e.currentTarget.dataset.foo; 这个让我想到了小程序,它内部实现也估计用了这个。 延伸题外话html5很重要,很多东西经常用到,我们应该怎么学习html5,我的体会是从html5名字去搜书名得到的书是不行的。但是有一本书,我最近翻了翻,发现里面有很多html5的内容,那就是大家都知道的《JavaScript高级程序设计》,很厚,一天读10页,也要很久读完,推荐大家如果忙的话,把它当字典查,当然最好是读一遍。 我之前项目搜data-name没搜到,后来看了mdn以后,我说不可能搜不到,肯定是之前没搜好,果然就是我的想法,搜到了,也是上述用法用的。 REACT16以后可以不用data-*,不带data-的属性也会渲染到原生dom节点上了 更多内容待大家一起探讨,下次见","categories":[],"tags":[{"name":"html5","slug":"html5","permalink":"https://choicelin.github.io/tags/html5/"}]},{"title":"日期+学习到的知识点","slug":"日期-学习到的知识点","date":"2018-01-30T13:26:20.000Z","updated":"2018-02-21T09:18:06.000Z","comments":true,"path":"2018/01/30/日期-学习到的知识点/","link":"","permalink":"https://choicelin.github.io/2018/01/30/日期-学习到的知识点/","excerpt":"","text":"2018-1-30 css filter: drop-shadow() sass draken() sass placeholder sass @include sass @content redux mapDispatchToProps dispatched function(bindActionCreator) react-router onEnter hook(20%) react-router IndexRoute IndexRedirect 高阶函数例子及定义 2018-2-13 understand es7 function bind syntax in react review this in ecmascript 2018-2-17 currentTarget <- represent registered element and equal to this in event handler target <- actual and real target in event understand function parameters transfer by value 2018-2-21 scope chain and excution context environment reference counting strategy of garbage collection","categories":[],"tags":[{"name":"学习记录","slug":"学习记录","permalink":"https://choicelin.github.io/tags/学习记录/"}]},{"title":"解决小程序音频自定义组件不同时播放问题","slug":"解决小程序音频自定义组件不同时播放问题","date":"2018-01-24T16:22:07.000Z","updated":"2018-03-11T08:23:04.000Z","comments":true,"path":"2018/01/25/解决小程序音频自定义组件不同时播放问题/","link":"","permalink":"https://choicelin.github.io/2018/01/25/解决小程序音频自定义组件不同时播放问题/","excerpt":"","text":"需求首先是一个播放的气泡,点击播放src音频,但是是一组列表,有许多这样的气泡,点其中一个播放后,再点其他的,前一个要停止播放。 分析这种其实是很好实现的,如果是直接写的话。但是现在用上了小程序的自定义组件,每个组件都是独立的没有联系,唯一的联系是父组件,这个时候,我们该怎么办呢。 解决方案由于涉及保密协议,就不上代码,说说思路。 思路: 用广播,如果该组件播放时,就发出一个广播,让其他组件关闭播放。 这里知识用到观察者模式(订阅与发布),具体怎么实现,有很多方式,本项目中的实现是我的师父写的,然而他写的有个地方有问题,他用了Promise处理监听的回调,然而Promise resolve以后,它的生命就到此为止了(个人目前的理解是这样的,如有勘误,请务必指出),所以只能执行一次回调,而不能多次监听,触发回调。我把这里改了,实现了一次监听,有触发随时执行。 关键问题,我怎么样只广播非当前播放的气泡呢。设一个属性,每次给一个变化的值进去,利用observer确定是当前组件,当前组件执行一个逻辑,这个逻辑就是避免被广播到 总结程序出不来,大部分是代码原因,继续搞,继续搞就能看到光明。 我的每篇文章都不长,但都可以说明一个问题,欢迎阅读。 -————–update分割线 —————– 最佳实践是这样的: 前端工作遇到的小知识点第14点,最好放在父组件中控制,上面操作太骚,而且后期处理起来不易维护","categories":[],"tags":[{"name":"工作经验","slug":"工作经验","permalink":"https://choicelin.github.io/tags/工作经验/"}]},{"title":"node流程控制之串行","slug":"node流程控制之串行","date":"2018-01-18T10:22:28.000Z","updated":"2018-01-18T10:33:02.000Z","comments":true,"path":"2018/01/18/node流程控制之串行/","link":"","permalink":"https://choicelin.github.io/2018/01/18/node流程控制之串行/","excerpt":"","text":"1234567891011121314151617181920212223242526272829function process1() { setTimeout(function() { var process1Result = 1; next(null, process1Result) },10)}function process2(data) { setTimeout(function() { if (data === 1) var process2Result = 2; next(null, process2Result) })}function process3(data) { console.log(data);}var taskList = [ process1, process2, process3]next(err, result) { if (err) throw err; var current = taskList.shift(); current && current(result);}next(); 流程控制 在node中让一组异步操作顺序执行的套路叫做流程控制,而这其中又分为串行流程和并行流程 串行流程 所谓串行流程,即一个接着一个做的流程叫做串行流程,上述代码即一个串行流程的实现。 首先有三个操作,顺序依次为process1,process2,process3, 整个过程的核心是这个next函数,跟node一些框架中交出控制权的next名字是一样的 思路就是按照索引的顺序执行函数,函数中异步调用的返回值,作为下一个next中执行的函数的参数,保证了数据不会丢失。","categories":[],"tags":[{"name":"node","slug":"node","permalink":"https://choicelin.github.io/tags/node/"}]},{"title":"Node编程基础","slug":"Node编程基础","date":"2018-01-11T05:59:29.000Z","updated":"2018-01-11T06:00:40.000Z","comments":true,"path":"2018/01/11/Node编程基础/","link":"","permalink":"https://choicelin.github.io/2018/01/11/Node编程基础/","excerpt":"","text":"Node编程基础如何组织代码 模块是Node让代码易于重用的一种组织和包装方式 传统方式按逻辑把单个文件分为多个文件,但这些文件互相引用的同时会产生全局变量污染的问题 Node模块打包代码是为了重用,但它们不会改变全局作用域 Node模块允许你从被引入文件中选择要你暴露给程序的函数和变量,如果模块返回的函数和变量不止一个,可以把它们作为exports对象的属性,否则就使用module.exports 如何创建模块 模块既可能是一个文件,也可能是包含一个或多个文件的目录。如果模块是一个目录,Node通常会在这个目录下找一个叫index.js的文件作为模块的入口 使用模块要用到require函数,require是Node中少数几个同步I/O操作之一 用node_modules重用模块 要求模块在文件系统中使用相对路径存放,对于组织程序特定的代码很有帮助,但是与他人共享代码却用处不大。Node的node_modules机制可以不必知道模块在文件系统中的具体位置。 node_modules的搜寻机制如下: 1.开始在文件同一目录下寻找; 2.没有找到,就在当前目录node_modules下找 3.没有找到,进入父目录,再找当前目录node_modules中有没有,重复3; 4.没有找到,找环境变量NODE_PATH指定目录中是否存在; 5没有找到,异常。 异步编程技术 事件发生会触发响应逻辑。 在Node事件里流行两种响应逻辑的管理方式: 回调 回调通常用来定义一次性响应逻辑。比如数据库查询,可以指定一个回调函数来确定如何处理查询结果。这个回调函数可能会显示数据数据库查询结果,根据这些结果做些计算,或者以查询结果为参数执行另一个回调函数 事件监听 事件监听器,本质上也是一个回调,不同的是,它跟一个事件相关联。在Node中,当有HTTP请求过来时,此时服务器会产生一个事件,事件监听器就可以监听这个事件,并添加一些响应的逻辑。 一个Node HTTP服务器实例就是一个事件发射器 能够继承和添加事件发射及处理能力的类(EventEmitter) (未完待续)","categories":[],"tags":[{"name":"Node","slug":"Node","permalink":"https://choicelin.github.io/tags/Node/"}]},{"title":"基本视觉格式化","slug":"基本视觉格式化","date":"2018-01-11T05:48:33.000Z","updated":"2018-01-16T05:27:22.000Z","comments":true,"path":"2018/01/11/基本视觉格式化/","link":"","permalink":"https://choicelin.github.io/2018/01/11/基本视觉格式化/","excerpt":"","text":"基本视觉格式化基本框 css假定每个元素都会生成一个或多个矩形框,这称为元素框。各元素框中心有一个内容区。这个内容区周围有可选的内边距,边框和外边距。 包含块(布局上下文) 每个元素都相对于其包含块摆放。正常流中的一个元素,其包含块由最近的块级祖先框、表单元格或行内块祖先框的内容边界构成。 正常流是指西方语言文本从左向右、从上向下显示。大多数元素都在正常流中,除了浮动或定位元素。 块级元素 一般一个基本框从左到右有这几个部分: 左外边界 左外边距 左边框 左内边距 左内边界 内容区宽度 右内边界 右内边距 右边框 右外边距 右外边界 一般地,一个元素的宽度(width)被定义为从左内边界到右内边界的距离,高度同理。高度和宽度属性都可以应用到元素。 不同的宽度、高度、内边距和外边距相结合,就可以确定文档的布局。 水平格式化 区分width和可见元素框的宽度,width属性是不包含padding和margin的(例外的是使用了CSS3的box-sizing),而可见元素框宽度是包含了padding和margin的 有个规则,正常流中块级元素框的水平部分总和就等于父元素width。这一句话即所有子元素的width加上它们的左、右内边距,边框和外边距就是父元素的width。 水平属性 水平格式化有7大属性,从左至右,分别是:margin-left, border-left,padding-left,width,padding-right,border-right,margin-right。这7个属性的值加在一起必须是元素包含块的宽度,这往往是块元素的的父元素的width。 这7大属性只有其中3个可以设置为auto(width,margin-left,margin-right) 使用auto(不考虑3个值以外的属性) 3个auto(margin-left,width,margin-right均为auto),其中width会占满父元素width,而margin-left,margin-right为0 2个auto(比如margin-left,width为auto),这时设置auto的属性会为0,width会将父元素width的剩余空间占满 1个auto,如果设置3个值其中一个为auto,那么设置auto的属性会确定所需的长度,保证3个值加起来等于父元素的width 0个auto同时三者设置的和超过父元素width,则margin-right会为auto。 (未完待续) 负外边距 外边距可以为负 7个水平属性总和要等于父元素的width,严格的说这个总和不能比包含块更宽。所以当外边距为负时子元素的width会超过父元素width,达到一种超出的效果 只要所有属性都是大于或等于0的,元素就不会大于父元素的内容区 百分数 值声明为长度还是百分数,并没有改变上述水平格式化规则","categories":[],"tags":[{"name":"css读书笔记","slug":"css读书笔记","permalink":"https://choicelin.github.io/tags/css读书笔记/"}]},{"title":"前端工作遇到的小知识点","slug":"前端工作遇到的小知识点","date":"2018-01-09T02:21:41.000Z","updated":"2018-04-29T22:09:24.000Z","comments":true,"path":"2018/01/09/前端工作遇到的小知识点/","link":"","permalink":"https://choicelin.github.io/2018/01/09/前端工作遇到的小知识点/","excerpt":"","text":"顶部子元素的margin-top直接到父元素上,目前简单的处理方法将子元素margin-top变成父元素padding-top,感觉其他方式也可以解决比如改变BFC,需要补一下BFC,还不太熟 微信小程序中,纵向的高度一定不要px,否则到不同屏幕上差异很明显,横向如果写滑动的效果,计算的left值可以用px,因为获取到的屏幕的宽度是一个以px为单位的值,用px才不会出现位置上偏移的问题。 文本溢出无法实现时,使用height,line-height,overflow做 做列表勾选同步时,前端修改了数据勾选状态,如果请求后端同步时失败,应回滚前端勾选状态,这中列表数据自带勾选状态的做法,也是一种性能优化的经验 接口全局配置,如果有两套环境(生产和测试),在页面里用到相关接口url,必须通过配置文件来引用,而不是直接写,如果有不同,就切分字符串,不然你绝对会不停的改来改去。 原生小程序,setData方法绝对不能set一个undefined值,否则真机会报一个null的错误,同时定位这种错误的方法是打console看这个null的log在console的前面还是后面来定位,巨坑哦 原生小程序组件在一个页面多次使用,并且要实现使用父组件的data作为组件的property,即在子组件要改变父组件data,来引起子组件properties变化,从而引起视图变化,达到一中父子组件通信的目的,可以充分使用triggerEvent方法,并且在触发时传入type字符串,以实现多态。 遇到一个问题,小程序在audio播放toggle时flag不生效,起初以为是flag的原因,结果是因为每次都生成了新的播放实例,在新的播放实例上调stop是无效的 calc运算符号是要空格的,要不然calc出不来; 小程序wxs出错弹层类型拿数据后会在真机直接奔溃,如果你确定template等传值都是对的,那么这个时候你需要看看wxs传入的值是否正确,这在控制台是不会报错的 列表点击弹出列表选取列表项逻辑应该是,点第一个列表时记录该列表项id,然后在第二个列表点确定时设置下一个id,并跑接口,取消则清空两个id; 生产环境小程序在打开时有两种情况,第一种是打开后到首页,这种无疑会触发onLaunch;还有一种是打开后进入其它页面然后关闭,再马上打开,这时还在之前显示的页面,这时并没有触发onLaunch; 小程序中innerAudioContext使用必须开启铃声状态才有声音哦,同时oncanplay可能会有问题 防止同时播放问题,保证运行的父组件中只有一个播放context,同时再播放前将其他全部标志设置为false cpm config set registry http://fjdsklajf.com; scss _前缀是用在一个文件不需要编译时,引入时可以去掉下划线 this.$parent就是app实例,在wepy的page中 调试一个框架的api,可以在方法里面console.log(arguments),看看需要传什么,然后可以相应知道怎么用 背景大圆弧可以用border-bottom-left-radius结合right来做,给具体的值 wepy中报找不到wxss目前发现了两个原因,第一子组件sass引用路径错误,第二命名使用了toast这种的保留组件名 小程序wepy框架 unexpected attribute near .sync(.once),检查是否在components中引入了组件 textarea的换行符,可以用white-space: pre-wrap显示生效 font-size,text-indent百分数值表示总是根据父元素继承的大小来计算,font-size可以继承(小程序button下子元素字体大小不能继承),继承的是计算值而不是百分数也就是计算了以后得到的具体的值,border,margin,padding,background不能继承,继承的值没有特殊性,连0都不如,比如通配选择器的样式就可以覆盖继承值 特殊性:id 0100 .some-class(类) *[alt](属性选择器) :first-child(伪类) 0010 img :before(伪元素)0001 text-indent只用于块级元素(如果块级首行有一个图像,则图像也会移动),同时可以为负 margin-top,margin-bottom的百分数值是相对于父元素宽度而不是高度,否则会出现高度地狱 行内非替换元素上下位置的margin现实上与没有是一样,同时负外边距时,上下外边距和行高不受影响 输入框要加样式,占满整行,并且要有高度 子view有margin-top会有滚动条,可以flex column 和min-height 100%,来解决 line-height居中与flex居中不要混用 laya中居中一个元素可以用宽度除以2和高度除以2 小程序里面的模板消息需要表单支持 此文长期更新,遇到什么就写什么,mark","categories":[],"tags":[{"name":"工作","slug":"工作","permalink":"https://choicelin.github.io/tags/工作/"}]},{"title":"html标签","slug":"html标签","date":"2018-01-05T05:24:08.000Z","updated":"2018-01-05T05:40:42.000Z","comments":true,"path":"2018/01/05/html标签/","link":"","permalink":"https://choicelin.github.io/2018/01/05/html标签/","excerpt":"","text":"htmls所有标签 a abbr acronym address applet area article aside audio b base basefont bdi bdo bgsound big blink blockquote body br button canvas caption center cite code col colgroup command content data datalist dd del details dfn dialog dir div dl dt element em embed fieldset figcaption figure font footer form frame frameset h1~h6 head header hgroup hr html i iframe image(请勿使用,从未实现) img input ins isindex kbd keygen label legend li link listing main map mark marquee menu menuitem meta meter multicol nav nextid nobr noembed noframes noscript object ol optgroup option output p param picture plaintext pre progress q rp rt rtc ruby s samp script section select shadow slot small Source spacer span strike strong style sub summary sup table tbody td template textarea tfoot th thead time title tr track tt u ul var video wbr xmp","categories":[],"tags":[{"name":"html","slug":"html","permalink":"https://choicelin.github.io/tags/html/"}]},{"title":"JS对象漫谈","slug":"JS对象漫谈","date":"2017-11-26T04:18:14.000Z","updated":"2017-11-26T04:22:56.000Z","comments":true,"path":"2017/11/26/JS对象漫谈/","link":"","permalink":"https://choicelin.github.io/2017/11/26/JS对象漫谈/","excerpt":"","text":"如何转为number Number(‘1’) === 1 parseInt(‘1’, 10) === 1 parseFloat(‘1.23’) === 1.23 ‘1’ - 0 +‘1’ 全局对象window 浏览器在加载时首先会创建window对象 window的属性分为两类: ECMAScript规定的和浏览器私有的(alert,prompt,confirm,console, history, document等) document属于dom规范范畴 ,history属于bom规范范畴 详解Number Number(‘1’) 类型转换 var n = new Number(1) 1 => {valueOf(): 1,..},通过valueOf获取到原始值1 使用直接量赋值 var n = 1;在早期是没有toString方法的,而现在直接使用toString会临时转换为Number对象包装后的“1”然后使用toString,最后把这个包装对象丢掉(磨砂次),这就是为什么给n.xxx = 1后,然后取n.xxx是undefined的原因 parseInt 与 toString进制问题 parseInt第二个形参传入一个进制,代表将第一个实参是什么进制的,然后转化为10进制输出 toString可以传入一个参数代表将要转化的进制,把调用者转化为对应进制的字符串 slice 为什么叫slice?外国人切面包叫slice,js slice可以“切”数组,字符串。 5个falsy值 0 NaN ‘’ null undefined false 所有对象都是真值 原型 var o1 = {}; o1.proto === Object.prototype var n1 = new Number(1) n1.proto === Number.prototype n1.proto.proto === Object.prototype proto 和 prototypeString.prototype是String的公用属性的引用 s.proto 也是String的公用属性的引用 var 对象 = new 函数(); 对象.proto === 函数.prototype 它们的值是 [Prototype] var obj = 函数.prototype obj.proto === Object.prototype var obj2 = 函数 obj2.proto === Function.prototype Function.proto === Function.prototype Function,prototype.proto === Object.prototype","categories":[],"tags":[]},{"title":"Hexo食用教程","slug":"Hexo食用教程","date":"2017-11-18T11:00:30.000Z","updated":"2017-11-18T11:36:22.000Z","comments":true,"path":"2017/11/18/Hexo食用教程/","link":"","permalink":"https://choicelin.github.io/2017/11/18/Hexo食用教程/","excerpt":"","text":"安装 npm install -g hexo-cli 建站 hexo init <你博客存储文件夹名字> cd <你博客存储文件夹名字> npm i example npm init blog cd blog npm i 修改配置文件_config.yml deploy: type: git repo: 部署 npm install hexo-deployer-git –save hexo deploy 写作 hexo new //新建文章 hexo g //生成静态件 hexo deploy //发布 feel interesting?do it!","categories":[],"tags":[{"name":"hexo","slug":"hexo","permalink":"https://choicelin.github.io/tags/hexo/"}]}]}