aototo / blog Goto Github PK
View Code? Open in Web Editor NEWAototo Blog
Aototo Blog
利用React Server Render 提高首屏的渲染速度
使用Reacr.renderToString
, React.renderToStaticMarkup
。
请将方法的bind一律置于constructor
,避免多次bind。
请只传递component需要的props ,切勿一股脑的<Component {...props} />
。
不需要传入状态的component写成const element
的形式,这样能加快这个element的初始渲染速度。
dom上设置可被react识别的同级唯一key
,否则情况可能不会重新渲染。
使用`Stateless Functional Component 无状态组件
React 的无状态组件优雅的实现可复用组件的方式。
栗子如下:
const Pane = (props) => <div>{props.children}</div>;
Pane.propTypes = {
label: React.PropTypes.string.isRequired,
children: React.PropTypes.element.isRequired
};
使用pureRender
,避免组件没有意义的渲染,配合immutable,减少渲染。
使用react-css-modules,解决了命名混乱,全局污染以及依赖管理的问题,多人协同开发有时候难免会发生样式上的冲突。
有个需要注意的地方,下面的2个顺序如果颠倒,就会出错。
@connect(mapStateToProps, mapDispatchToProps)
@CSSModules(styles)
把这个按需写着这里,本身需要react-router支持,索性就放在这边了。
加载函数:
require.ensure(dependencies, callback, chunkName)
//这里react-router 使用require.ensure,当然了webpack需要配置一下。
<Route path="home" getComponent={(location, callback) => {
require.ensure([], require => {
callback(null, require('modules/home'))
}, 'home')
}}></Route>
具体看webpack官方
看不懂看这篇webpack 按需打包
项目数据扁平化,不扁平化带来的问题:
通过redux 的combineReducers
可以很好的扁平化数据。如果使用immutable的话整个侵入性非常的强,不仅要修改combineReducers(因为combineReducers实现就是可变的数据),还需要注意获取数据的时候是否是不可变,以免是null。如果使用immutable可以推荐使用redux-immutable。
return {
...state,
newData
}
上面这种写法本身也算是一种immutable,但是要求数据层级不能太深。如果数据相对复杂建议使用immutable。
推荐pure-render-decorator方便的来控制组件渲染。
import pureRender from 'pure-render-decorator';
@pureRender
class ...
使用immutable的时候数据转换如下:
immutableJs
Immutable 详解及 React 中实践
redux-immutable
seamless-immutable体积更小,兼容相对好。只支持Arrays and Objects。
reselect
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
每当store发生改变的时候,connect就会触发重新计算,为了减少重复的不必要计算,减少大型项目的性能开支,需要对selector函数做缓存。推荐使用reactjs/reselect, 缓存的部分实现代码如下。
export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
let lastArgs = null
let lastResult = null
const isEqualToLastArg = (value, index) => equalityCheck(value, lastArgs[index])
return (...args) => {
if (
lastArgs === null ||
lastArgs.length !== args.length ||
!args.every(isEqualToLastArg)
) {
lastResult = func(...args)
}
lastArgs = args
return lastResult
}
}
If the values of the input-selectors are the same as the previous call to the selector, it will return the previously computed value instead of calling the transform function.
假如state.todos 中todos 提供的数据没有发生改变,就会return之前计算好的结果,这样就可以少去非常多的计算成本。
具体的实现可以去看https://github.com/reactjs/reselect#creating-a-memoized-selector。
具体用法:以下修改reudx官方的的demo
import { createSelector } from 'reselect'
const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos
export const getVisibleTodos = createSelector(
[ getVisibilityFilter, getTodos ],
(visibilityFilter, todos) => {
switch (visibilityFilter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
)
如果我们需要同时发送很多action,比如:
dispatch(action1)
dispatch(action2)
dispatch(action3)
可以减少不必要的计算,推荐用到redux-batched-actions
dispatch(batchActions[action1, action2, action3])
源码很简单
https://github.com/tshelburne/redux-batched-actions/blob/master/src/index.js#L7
开发中使用DevTools,建议使用谷歌的插件,不建议在页面结构中插入DevTools。
redux-devtools-extension
在开发环境以及产品环境中移除devTools,避免不必要性能开销和文件大小。
栗子如下:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./configureStore.prod');
} else {
module.exports = require('./configureStore.dev');
}
注意: 需要在webpack中使用DefinePlugin
插件。
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
阮一峰的redux 入门
ReactJS组件间沟通的一些方法
聊一聊基于Flux的前端系统
React 性能工程
聊一聊基于Flux的前端系统
React性能工程-- 深入研究React性能调试
A Better File Structure For React/Redux Applications
React服务器端渲染实践小结
dva 项目解决方案
Getting Started with Redux
React 实践心得:react-redux 之 connect 方法详解
REACT&REDUX中SCROLL LIST封装实践
redux-axios-middleware axios兼容ie9,提供promise ,很方便。
深入理解 react-router 路由系统
Immutable 详解及 React 中实践
webpack+ react-router 按需加载
大概的整个项目具体的优化就这些,细节的插件和实施大家自己去看文档。后续继续更新,喜欢的朋友star支持一下。
在使用wechaty 的时候,由于headless chrome 在linux 环境难安装,所以改用了 docker run
先安装docker
http://www.runoob.com/docker/centos-docker-install.html
拉取镜像的命令:
$ docker pull registry.docker-cn.com/zixia/wechaty
将Wechaty版本号指定到最新的版本
$ docker tag zixia/wechaty:latest
接下来跑就可也了
$ docker run -ti --rm --volume=$(pwd):/bot zixia/wechaty index.js
-d, --detach=false :指定容器运行于前台还是后台,默认为false
--volumes-from=[] : 给容器挂载其他容器上的卷,挂载到容器的某个目录
--rm=false : 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
一般执行后台(ps: -d -m 不能一起使用)
$ docker run -ti -d --volume=$(pwd):/bot zixia/wechaty index.js
查看后台进程
$ docker ps
找到 CONTAINER ID
然后
$ docker logs -f --tail=all CONTAINER ID
就可以查看log 信息了
记住docker 需要绑定端口映射
-p 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort。
比如
$ docker run -d -p 5000:5000 training/webapp app.js
react router 核心 就是使用 history 模块。
so..
可以在 其他需要访问控制路由的地方
var history = require('history');
// 如果你的路由是: example/some/path 这样真实的 URL
import { createBrowserHistory } from 'history'
const history = createBrowserHistory()
// 如果你的路由是 :example/#/some/path
import { createHashHistory } from 'history'
const history = createHashHistory()
history.push(example/some/path)
维护,测试组件是一件比较麻烦的事情,必须有使用接口文档,还有开发组件过程中不断测试的问题。storybook是一个能解决这一切问题的好工具。
github storybook地址
cd my-react-app
npx -p @storybook/cli sb init
会在项目根目录出现几个文件夹
.storybook
stories
需要在.storybook 文件夹下 创建webpack.config.js 去新建loader 插件加载less 资源文件等
const path = require("path");
module.exports = (storybookBaseConfig, configType, defaultConfig) => {
storybookBaseConfig.module.rules.push({
test: /\.less$/,
loaders: ["style-loader", "css-loader", "less-loader"],
include: path.resolve(__dirname, "../")
});
storybookBaseConfig.module.rules.push({
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
include: path.resolve(__dirname, "../")
});
storybookBaseConfig.module.rules.push({
test: /\.(png|jpe?g|gif|svg|ico)(\?.*)?$/,
loader: 'url-loader',
include: path.resolve(__dirname, "../")
});
return storybookBaseConfig;
};
./stories/index.stories.js 文件添加组件
具体内容如下
https://storybook.js.org/basics/writing-stories/
插件添加
storybook 有许多可以用的插件比如notes ,还可以支持markdown 文件导入
notes 文档写的很清楚了
需要在.storybook 文件夹下 创建插件模块,导入使用
全局的style css
需要自己引入,这点比较麻烦。需要添加配置的在
./.storybook/webpack.config.js
修改添加就可以了
推荐插件 markdown
1、过滤器
配合moment.js 日期插件过滤时间, 非常方便
filters: {
moment: function (data) {
if (!data) return ''
return moment(parseInt(data)).format('LL')
}
}
<span>{{ date | moment }}<span/>
2、 v-if v-show
v-if 是“真正”的条件渲染,v-show 就简单得多基于 CSS 进行切换
如果频繁的切换元素,请使用v-show
3、避免 v-if 和 v-for 用在一起
v-for 的优先级大于 v-if
js通过构造函数来类,因为js没有类, 通过原型链实现继承,或者通过复制属性
//创建一个Dog的构造函数, 构造函数大写开头。
function Dog() {
}
// 通过注入prototype属性,来实现对象需要共享的属性和方法
Dog.prototype.call = function() {
alert("汪")
}
//创建哈士奇,继承狗
function Husky(name) {
this.name = name;
}
Husky.prototype = new Dog(); //这里是原型链矫正
Husky.prototype.variety = "哈士奇";
var variety1 = new Husky("小哈")
具体的内容太多了,推荐一篇文章讲的很棒
浅谈浏览器http的缓存机制
上下相邻的普通元素,上下margin边距取其中较大的边距值。
比如:
div.top {
margin-bottom: 30px;
}
div.bottom {
margin-top: 30px;
}
// 此时它们之间还是只有30px。
第一种: 触发BFC就可以解决margin重叠的现象,可以通过以下触发BFC:
关于BFC可以看这篇文章,BFC详说
第二种: 解决margin重叠的可以用padding代替margin
在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移,其效果与印刷排版中的文本环绕相似。浮动元素脱离文档流,不占据空间。
清除浮动可以让元素实现BFC来解决,overflow: hidden 或 overflow: auto 触发浮动元素父元素的 BFC 特性,从而可以包含浮动元素,闭合浮动。
清除浮动的技巧:
1.使用空标签清除浮动,定义css clear:both
. 问题增加了无意义标签。
2.浮动元素的父标签添加css属性overflow:auto; zoom:1; zoom:1
用于兼容IE6。
3.使用after伪对象清除浮动。代码如下
.clearfix {
zoom: 1;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
利用函数编程的惰性求值, 下面是我写的
function add() {
//把arguments转成array对象
var _args = Array.prototype.slice.apply(arguments);
return function() {
if ( arguments.length > 0 ) {
var newArgs = Array.prototype.slice.apply(arguments);
_args = _args.concat(newArgs);
return add.apply(null, _args)
}
return _args.reduce(function(a, b) {
return a + b;
})
}
}
var a = add(1,2,3)(2)
内容大家都懂不多说了
下载地址:
https://www.torproject.org/projects/torbrowser.html.en
tor 网络设置
选择内置网桥 -> obfs4
使用代理,这里我使用SOCKS 5 , 默认是127.0.0.1 , 端口是1080
然后去玩吧.....
由于闭包[[Scope]] 属性包含与执行环境作用域链相同的对象引用,函数活动对象本来会随着执行环境完毕后一同销毁,但引入闭包,对象无法被销毁。
闭包会造成更多的内存开销,同时IE下还会造成内存泄露。
在同一个函数中,如果存在多次读取同一个对象成员,可以在局部函数中保存对象,减少查找。
function getWindowWH() {
var elBody = document.getElementsByTagName('body')[0];
return {
width: elBody.offsetWidth,
height: elBody.offsetHeight
}
}
同时也解决了属性越深,访问速度越慢的问题。
访问DOM的次数越多,代码运行的速度越慢,统一的保存结果最后在一并输出。 例子:
function innerHTMLLoop() {
var content = '';
for (var count = 0; count < 10000; count++){
content += 'a';
}
document.getElementById("idName").innerHTML += content;
}
重绘(repaints)与重排(reflows)
当页面布局和几何属性改变时就需要"重排"
避免在修改样式的过程中使用 offsetTop, scrollTop, clientTop, getComputedStyle() 这些属性, 它们都会刷新渲染队列
最小化重绘和重排, 尽量一次处理
documentFragment
当有很多元素需要绑定事件的时候,我们一个一个的去绑定事件是有代价的的,元素越多应用程序越慢。事件绑定不但占用了处理时间,并且追踪事件需要更多的内存,有时候很多元素是不需要,或者是用户不会点击的,所以我们需要使用事件委托来解决没有必要的资源消耗。
例子: 我们需监听li的click事件,通过冒泡事件来获取点击的对象。
<ul>
<li index='1'>1</li>
<li index='2'>2</li>
<li index='3'>3</li>
</ul>
var ul = document.getElementById('ul');
ul.addEventListener('click', function(e) {
var now_index = e.target.getAttribute('index');
...
})
一般的for语句可能很多人都这么写
for(var i = 0; i < array.length; i++){
....
}
每次循环的时候需要查找array.length,这样不但很耗时,也造成性能损失。只要查找一次属性,存储在局部变量,就可以提高性能。
for(var i = 0, len = array.length; i < len; i++){
....
}
重写后的循环根据数组的长度能优化25%的运行时间,IE更多。所以平时书写的时候还是要多加注意。同时还是要避免使用for-in循环。
if-else 对比 switch, 当条件语句较多的时候switch 更易读,运行的要更快。所以但条件少的情况下使用if-else,当条件增加时,更倾向于switch,会更佳合理。
通过避免使用eval()和Function()构造器来避免双重求值带来的性能消耗。比如:
eval('2 + 2');
直接量的速度回更快。
//bad
var myObject = new Object();
myObject.name = "xxxx";
//good
var myObject = {
name: "xxxx"
}
https://docs.npmjs.com/cli/audit.html
npm audit 是 npm 6 新增的一个命令,可以允许开发人员分析复杂的代码并查明特定的漏洞。在刚刚发布的 npm 6.1.0 版本中,开发团队对该命令进行了完善。现在可使用 npm audit fix 子命令自动修复检测到的漏洞,而不必再自己进行跟踪和修复。
当你npm audit的时候 会提示:
found 15 vulnerabilities (6 low, 8 moderate, 1 high) in 2810 scanned packages run
npm audit fixto fix 7 of them.
会提示 6 low, 8 moderate, 1 high
出现 high 就要注意了
$ npm audit fix --force
执行下命令,如果还有存在 high , 需要更替包的方案
yarn 也可以使用
更新yarn到 1.16.x 版本
执行
yarn audit
检查包 yarn audit fix -- force
修复 (删除已经存在的yarn.lock 和 node_module 包)
这个一开始没注意,踩了坑!
反正没办法通过Component 定义的组件去修改引入第三方组件的样式,除非用标签选择器!不过这种做法也不好。
这是微信的相关的文档描述:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html
可以在外层的app.wxss 修改。也不好管理
或者设置下面值
-isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值)
apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。(这个选项在插件中不可用。)
或者使用 外部样式类
externalClasses
你懂的...
https://github.com/rofl0r/proxychains-ng
$ brew install proxychains-ng
找到 /usr/local/etc/proxychains.conf
文件
将socks4 127.0.0.1 9095
改为 socks5 127.0.0.1 1080
socks5 127.0.0.1 1080 是 shadowsocks的代理
最后在你的命令之前 添加 proxychains4
$ proxychains4 node xxxxx.js
GitHub博客 https://github.com/asd0102433/blog 喜欢的start,长期更新
我们需要在页面插入图片:
index.html
<img src = 'assets/images/xxx.jpg' />
因为这个图片webpack并没有被加载进来,会得到404的结果。
通常的做法是在对应的js中require
该资源
require('./assets/images/xxx.jpg');
这样就在内存中可以访问到内存中图片。(但是要注意文件加载的路径问题,最简单的就是按照构建的生产环境的资源路径)还要注意图片是否有hash值。
以下是js输出的图片资源路径
./images/acb790246029d8f127588eacd3005fbei1.jpg
比如最后生成的资源路径。页面也需要按照这样引入,显然是不可能的。
<img src = './images/acb790246029d8f127588eacd3005fbei1.jpg' />
当一个项目静态图片多的时候如何处理?比如一个不需要后端图片数据的前端活动页面?接下来有三种方法。
CopyWebpackPlugin
copy-webpack-plugin
把静态资源都拷贝到构建目录。使用方法也非常简单。
const CopyWebpackPlugin = require('copy-webpack-plugin');
new CopyWebpackPlugin([
{ from: path , to: bundle_path }
])
这样就可以在页面中愉快的使用目录地址了(注意的是资源路径按照最终构建完成的目录)
如下是构建完毕后的目录
| dist
| ---- | images
| ---------- | xxx.jpg
| ---- | index.html
<img src='./images/xxxx.jpg' alt="">
CopyWebpackPlugin
能把全部静态资源全部拷贝过去,从而优化webpack在构建上面的速度,减少时间,是一个不错的优化方案,可以建议使用。
<img src='<%= require("./assets/images/xxx.jpg") %>'>
这种方法跟import是一样的,我们可以直接使用它,还可以配置alias,来简化这个path的名字
resolve: {
alias: {
imageBase: '../app/assets/images'
},
},
设置后页面中可以使用alias来代替路径问题
<img src='<%= require("imageBase/xxx.jpg") %>' >
同事css也可以使用,配合~
background: url('~imageBase/xxx.jpg')
此时~告诉webpack这是一个module,而不是相对路径。
webpack-contrib/css-loader#49
alias
的使用同样也可以让构建的速度提升,直接锁定资源的地址,从而减少搜索的耗时。
目前比较省力的方案
{
test: /\.html$/,
loader: 'html?attrs=img:src img:data-src'
}
它默认处理html中的<img src="image.png">
为require("./image.png"),
同时还处理了hash文件名的问题。但是html-loader不支持下划线模板。会导致HtmlWebpackPlugin的变量模板失效。
具体问题:
jantimon/html-webpack-plugin#174
jantimon/html-webpack-plugin#223
以上的方法可以结合使用,比如2,3中使用的话,不但简化了图片的路径问题,还优化了构建速度,同时引入静态资源也非常的简单。
例子:
<img src='~imageBase/xxx.jpg' alt="">
Portals 提供了一种很好的方法,将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点。
官方文档:http://react.html.cn/docs/portals.html
如果说 ReactDOM.render and ReactDOM.createPortal 有什么区别
有2个月没怎么写文章了,这次项目做了一个HTML5的在线实时游戏,游戏是基于
Socket.IO架构写的,网上的资料也很多,比较零散,啰嗦。在这里总结下整个流程,开拓思路。(PS: 主要是整个流程的思路,不讲框架的基础用法)
var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
app.listen(8080, function() {
console.log('Listening at localhost:8080')
});
var room = {};
// 客户端连接后处理事件
io.on('connection', function(client) {
....
});
// 客户端:随机生成一个房间的keyId,然后发送到后端
var socket = io.connect();
socket.emit('create', roomId);
// 服务端:
io.sockets.on('connection', function(client) {
//接受create事件 ,并且加入roomId
client.on('create', function(roomId) {
// 可以在这里记录roomId, 并且保存到rooms数组里
// rooms.push(roomId)
client.join(roomId);
});
});
可以使用qrcodejs来生成二维码。具体的步骤就是生成一个带有code的Url。玩家根据Code来判断是否有房间。有则加入,没有则创建。
生成Url
new QRCode(document.getElementById("qrcode"), location.href + 'play.html?id=' + ID);
通过扫描二维码,得到location.search里roomId,然后发送到后端,加入房间
var roomId = location.search.replace('?id=', '');
// 客户端: 得到room id
socket.emit('join', {
roomId: roomId,
...
});
// 服务端
io.sockets.on('connection', function(client) {
...
client.on('join', function(data) {
client.join(data.roomId);
// 通知room房间里面的其他玩家
io.in(data.roomId).emit('new_player', data);
});
...
}
服务端接收join事件,客户端加入房间后,同时通知房间里面的其他玩家。
//node.js
var url = client.request.headers.referer
每次玩家发送一次请求的时候,无需每次都带上房间的id。
每个玩家都会生成一个固定的user Id, 保存在server 和 客户端的localStorage 里面
user = {
id: null,
rooms: [
{ roomId: null, data: null}
]
}
每一个玩家在连接后端的时候都可以创建一份数据,用来恢复掉线后的数据。
具体大概的流程都在上面了,socket的具体使用可以参考官网的demo, 这边只是一个大概的流程思路。具体的逻辑代码也没什么好讲的,比如玩家做了动作
emit (客户端) -> on (服务端) - emit -> on(客户端),很简单的。
如果使用$('body').scrollTop() 获取的值是0,谷歌Translate翻译插件就是这样失效的。
使用documentElement || body 来兼容。
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
或者使用
$(window).scrollTop()
之前使用$('body').scrollTop() 的插件,需要修改了
花了一天时间看了redux 几个function的源码感触颇深,接下来总结下源码的作用。
compose.js
https://github.com/reactjs/redux/blob/master/src/compose.js#L21
return funcs.reduce((a, b) => (...args) => a(b(...args)))
一头雾水啊,函数编程功力不够,第一眼懵逼,细细琢磨才领悟其中的奥妙。
先不谈奥妙之处我们先写段代码,慢慢解析。
下面代码我们需要组合2个函数,构建成一个新的函数
var f1 = function(a) {
return a * 10;
}
var f2 = function(a) {
return a + 1;
}
function compose(f1, f2) {
return function(x) {
return f1(f2(x))
}
}
var number = compose(f1, f2)(4)
number == 50 // true
让代码从右向左运行变成f1(f2(x)),4 -> f2 -> f1 -> 50,当然了你也可以在内部reverse一下让函数从第一个开始执行。
接下来我们看下实际运用的compose
let compose = (...funs) => (result) => {
for (var i = funs.length - 1; i > -1; i--) {
result = funs[i].call(this, result);
}
return result;
}
var number = compose(Math.round, parseFloat)('5.8');
// number => 6
parseFloat函数return的结果作为Math.round函数的参数
Math.round(parseFloat('5.8'))`
我们在回到redux compose.js
funcs.reduce((a, b) => (...args) => a(b(...args)))
跟上面的compose 的原理其实是一样一样的,就是写法很精妙。
先分析reduce
这个method
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
reduce()把返回的结果继续和序列的下一个元素做累积计算,其效果就是上面这样。
拆分上面的源码步骤如下:
第一次 返回 (...args) => a(b(...args) 且与下一个c函数做计算
第二次 a 等于 (...args) => a(b(...args) ,b 等于 c 这个function , 然后再一次做 (...args) => a(b(...args)) 计算,就形成了
(c(...args)) => a(b(...args))
//返回 a(b(c(...args)))
第三次也是如此
(d(...args)) => a(b(c(...args)))
//返回 a(b(c(d(...args))))
结果就如注释的一样
// from right to left. For example, compose(f, g, h) is identical to doing
// (...args) => f(g(h(...args))).
到这里我们这点了compose 的实现原理,接下来我们看下applyMiddleware.js
中如何实现中间件的。
源码
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
然后我们看下middleware函数的签名
({ getState, dispatch }) => next => action => {...}
第一步把middlewares数组里的函数带着middlewareAPI这个参数都挨个执行个遍且,且保存在chain数组中。也就是middleware中的第一个参数,返回 next => action => {...}
第二步 就是上面composeJs 中组合chain中的函数。变成f1(f2(f3(store.dispatch))),并且最终保存在dispatch 变量中。里面的函数最终会返回action => {...} 这个函数 作为上一个函数的next参数保存在上个函数的scope中,所以每个middleware 中必须执行next(action)才能进入下个middleware。而最后一个middleware传入的next正是原始的store.dispatch。
//演示代码
const f1 = ({ getState, dispatch }) => next => action => {
...
}
compose(f1)(store.dispatch)
dispatch = (action) => { /* next = store.dispatch */ }
compose(f1, f2)(store.dispatch)
// f1 = next => action => { ... }
// (action) => { /* next = store.dispatch */ } 作为f2的返回值赋值给next 参数
//其中的next就是f2的函数体
基本的源码分析到此就差不多了,要注意的是为什么异步middleware中可以使用dispatch让action在重新执行一遍。因为其中的dispatch就是源码中的
dispatch = compose(...chain)(store.dispatch)
这样异步middleware就非常容易理解,也很好巧妙的实现异步流。而next则是进入下一个middleware。如果在middleware里面滥用store.dispatch,就会造成无限的循环。
const f2 = store => next => action => {
next(action) //进入下一个middleware
store.dispatch(action) //从左边的第一个middleware开始
}
redux-thunk的实现就是判断action是否是function,如果是就执行action(),action就可以是一个异步请求的函数,具体可以看https://github.com/gaearon/redux-thunk/blob/master/src/index.js#L3的源码!
就写到这里,函数编程的巧妙还需多多学习呀,不足之处还请指正。
参考
Middleware
viewport-fit
有文章了 懒得写了,记录一下
https://aotu.io/notes/2017/11/27/iphonex/index.html
安装git后我们需要配置一下,告诉git我们的基本信息等等..一般在用户范围内去配置 git ,也就是在 global 范围。
global
全局设置 $ git config --global user.name 'xxxx'
$ git config --global user.email 'xxxx'
同时我们可以用 $ git config --list
来查看我们的设置,
如果需要修改重新设置使用
$ git config --unset --global user.name
全局范围的配置会保存在当前用户的主目录下面 叫.gitconfig 的文件里面。我们可以使用cat ~/.gitconfig
来查看
$ cat ~/.gitconfig
通过别名可以简化经常输入的内容,别名的配置也可以在gitconfig里面查看
git config --global alias.cm commit
这样当你在commit 的时候 就可以 使用 git cm -m 'xx'
来代替了。
有时候我们需要去忽略系统生成的文件比如mac 下的.DS_Store,我们可以在全局设置gitignore
git config --global core.excludesfile ~/.gitignore_global
然后在gitignore文件里面写入需要忽略的文件
.DS_Store
你可以参考下面的链接来看更多要忽略的文件
https://gist.github.com/octocat/9257657
如果在项目中可以创建名为.gitignore
,为每个项目配置。具体忽略的文件可以查看上面的链接
如果你想忽略掉已经跟踪的文件,可以使用 git rm cached
选项,再指定一下文件名称,这样可以取消跟踪指定的文件。
初始化Git
控制和管理项目需要初始化git
$ git init
查看当前修改 -git status
$ git status
查看提交的信息 -git log
$ git log
$ git log --oneline //显示在一行
$ git log --oneline --before = '2017-01-01' //显示2017-01-01的提交
你可以使用git help log
查看更多信息
提交 -git commit
添加新的文件,或者修改已有的文件,之后使用commit告诉 git 你做了哪些事情。可以使用git log 查看以往的提交。
$ git commit -m '这里来描述事情'
对比差异 -git diff
如果你修改了文件,你想查看自己目前修改的了哪些,可以使用
$ git diff
如果想跟暂存区里面的文件进行对比(暂存区就是git add 文件),可以用
$ git diff --staged
重命名,移动 -git mv
git mv可以重命名或者移动文件和目录
$ git mv xx1.js xx2.js //重命名
$ git mv xx1.js js/ //移动文件
$ git mv js asset/ //移动目录
删除文件 -git rm
$ git rm fileName
如果你想删除暂存区的可以使用
$ git rm --cached fileName
撤销操作 -git amend
如果提交commit之后发现有遗漏的文件可以使用该命令,具体流程
$ git conmmit -m "xxx.js"
$ git add "xxx2.js"
$ git commit --amend --no-edit
上面的三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。提交之后你就可以使用git log --name-status
提交了哪些文件
修改撤销文件 -git checkout
比如你删除了index.html,使用以下命令恢复文件,也可以恢复之前修改过的
$ git checkout -- index.html
$ git checkout . //会取消所有本地的
撤销已经add的文件 -git reset
有时我们会不小心git add,取消某些add的文件。(还原暂存区)
$ git reset HEAD fileName
撤销已经commit的文件 -git reset
假如你的项目已经commit了,可以用到该命令恢复到指定的commit。
第一步 git log --oneline
5c422a3 add html
6804500 Delete a.css
第二步
$ git reset id //id 是指上面commit 的id
第二种方法,更快捷的
$ git reset HEAD^ // 回退到上一个commit, 默认--mixed
git reset 有3个选项,
--soft
不会影响到工作目录还有暂存区里的东西
--hard
工作目录,暂存区直接重置到指定的提交状态
--mixed
默认选项,会把暂存区里的东西重置到指定提交状态,并且指针指向这个提交。
一般情况, 如果你发现commit文件是存在bug情况,你只需要修改文件代码,那就用默认的mixed,hard会重置文件的内容到指定的commit,也就是说你的之前写的代码会被重置删除掉,切记。
git reset [--soft | --mixed | --hard] [-q] [<commit>]
保存修改恢复进度文件 -git stash
stath 可以让工作进度先保存起来,需要用到的时候在恢复。
场景: 你修改文件app.js ,然后你git add file后你暂时不想跟后面的文件一起commit,那么就可以先储存起来
$ git stash save "这里可以是说明"
查看保存的进度,或者显示进度的目录
$ git stash list
$ git stash show -p stash@{..}
恢复进度(取出之前保存的进度)
$ git stash apply stash@{..}
删除不需要的进度
$ git stash drop 或者 后面跟上stash@{..}代号
查看,创建项目分支
$ git branch
$ git checkout -b [name_new_branch]
删除分支
$ git branch -d [name_branch]
切换分支
$ git checkout [branch-name]
对比分区的区别
$ git checkout branch
分区合并
$ git merge [your_branch]
注意:如果你合并master ,首先需要切换到master 分支下进行合并。
分区对比
$ git diff [branch]..[branch]
重命名branch
$ git branch -m [branch] [new_name_branch]
添加远程地址
$ git remote add origin [git_address]
分支推送到远程的版本
$ git push origin master
具体很多细节去多看看深入的实战
git log 命令支持选项参考这里
选项 | 说明 |
---|---|
-p | 按补丁格式显示每个更新之间的差异 |
--word-diff | 按 word diff 格式显示差异。 |
--stat | 显示每次更新的文件修改统计信息。 |
--shortstat | 只显示 --stat 中最后的行数修改添加移除统计。 |
--name-only | 仅在提交信息后显示已修改的文件清单。 |
--name-status | 显示新增、修改、删除的文件清单。 |
--abbrev-commit | 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。 |
--graph | 显示 ASCII 图形表示的分支合并历史。 |
--relative-date | 使用较短的相对时间显示(比如,“2 weeks ago”)。 |
--pretty | 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。 |
--oneline | --pretty=oneline --abbrev-commit 的简化用法。 |
更多的深入了解可以去git api查看
参考
git 官网
5.2 代码回滚:Reset、Checkout、Revert的选择
git 中文
Create a new branch with git and manage branches
官方文档:
https://mobile.ant.design/docs/react/customize-theme-cn
问题:ant-mobile 使用 px 作为样式基本单位,在750 vw 方案下,内容会被缩小,需要把基础变量提升2倍,以便支持高清模式。
制定方案:使用 modifyVars 的方式来覆盖变量。
适用:webpack 3+ 以上
代码
第一步:配置 babel-plugin-import 确保加载 antd-mobile less 文件, package.json 如下
style
为true时候,引入 less 文件,等于 'css' 时候 加载编译好的 css 文件,这里要设置less,才可以修改变量
{
...
"plugins": [
["import", {"libraryName": "antd-mobile", "style": true}],
]
}
二步:修改les-loader , 不同的webpack 会不一样的配置,如果是create-react-app 生成的项目,需要修改webpack.config.js 中 getStyleLoaders函数
{
loader: 'less-loader',
options: {
modifyVars: {
"hd": "2px"
},
javascriptEnabled: true // 不开启会报错
}
},
// 如果是create-react-app, 进入webpack.config.js, 找到 getStyleLoaders函数
if (preProcessor) {
let options = {
sourceMap: isEnvProduction && shouldUseSourceMap
}
if (preProcessor === 'less-loader') {
/* 导入 antd mobile Theme 主题 */
options = {
...options,
modifyVars: antdMobileTheme,
javascriptEnabled: true
}
}
loaders.push(
...
{
loader: require.resolve(preProcessor),
options: options
}
...
);
}
它现在处于 stage-1 阶段。你可以在.babelrc文件中引入 @babel/plugin-proposal-optional-chaining插件来使用它。
可以 这样获取数据
const data = myObj?.value?.xxxxx
observable 用来观察对象,数组,类等数据结构
使用方式:
例子:
import {observable} from 'mobx';
var name = observable('aotu');
//ES7 装饰器的使用
class Aotu {
@observable name = 'aotu'
}
使用autorun去观察依赖的相关数据的变化(下面demo中只有name的值发生改变才会触发该函数
)
var disposer = autorun(() => console.log(name.get()));
更多匹配类型应用转换规则
https://cn.mobx.js.org/refguide/observable.html
创建计算值, 并返回,纯函数形式,内部做了优化,当参数相同时候不从新计算
// ES7 装饰器的使用
import {observable, computed} from "mobx";
class Sum {
@observable price = 1;
@computed get total() {
return this.price * 10;
}
}
// 不使用 decorate
import {decorate, observable, computed} from "mobx";
class Sum {
price = 1;
get total() {
return this.price * 10;
}
}
decorate(Sum, {
price: observable,
total: computed
})
把一些修改状态的fn放在action里面,以便更好的管理代码,(严格模式开启,限制其他地方随意改变需要的值,降低不确定性)。
例子
import {observable, action} from 'mobx';
useStrict(true);
class Store {
@observable number = 0;
@action add = () => {
this.number++;
}
}
const newStore = new Store();
newStore.add();
runInAction 其实就是action的语法糖action(fn))()
主要用在异步的回调时候立即执行,action只会影响当前执行的动作。
例子:
useStrict(true);
class Store {
@observable userName = '';
@action load = async () => {
const data = await getUserData();
runInAction(() => {
this.userName = data.userName;
});
}
}
所提供的函数总是立即被触发一次, 而当观测到的数据发生变化的时候,如果变化的值处在autorun中,那么autorun就会自动执行。
class User {
@observable name;
@action
setName(name) {
this.name = name
}
}
let user = new User()
user.setName('aotu')
//每次修改打印name ,值相同不打印
autorun(() => console.log(user.name))
Reaction是autorun 的变种,对于如何追踪 observable 赋予了更细粒度的控制。接收两个函数参数,第一个(数据 函数)是用来追踪并返回数据作为第二个函数(效果 函数)的输入。 不同于 autorun 的是当创建时效果 函数不会直接运行,只有在数据表达式首次返回一个新值后才会运行。 在执行 效果 函数时访问的任何 observable 都不会被追踪。
class User {
@observable user = {
name: '',
age: 0
};
@action
setName(name) {
this.user.name = name
}
@action
setAge(value) {
this.user.age = value
}
}
//只有name发生变化的时候,才会执行该函数,age发生改变则不会运行
let userReaction = reaction(
() => user.name,
(value) => console.log(value)
);
reaction 返回一个清理函数,可以在不需要的时候,运行返回函数,节省内存开销
//直接执行
userReaction()
后续更新...
参考:https://cn.mobx.js.org/
Note: If you do not specify a font size, the default size for normal text, like paragraphs, is 16px (16px=1em).
so :1rem = 16px
设置 html { font-size: 20px }
so 1rem = 20px
假如设计稿750,设置1rem 为 100px
。
// 换算rem
let baseFontSize = 100
px2rem (num) {
return num / 100
}
// 动态修改根类html 的 fontsize
const baseDesign = 750
changeView () {
fontSize = window.innerWidth / 750 * baseFontSize
}
设计稿 750 px, 1w 等于 7.5 px
const baseDesign = 750
px2vw (num) {
return = (num / baseDesign * 100) +'vw'
}
使用 Editorconfig
来解决不同人的缩进风格
atom 编辑器
https://github.com/sindresorhus/atom-editorconfig
需要在项目根目录创建一个.editorconfig
文件
下面是一个简单的demo.(ps: 这玩意要重启一下,很玄乎)
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
[*/*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
[*.{json,yml}]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
修改 indent_size 数值可以修改缩进的大小。
ps: atom的缩进设置在 * setting -> setting -> Editor Settings * 里面, atom-editorconfig 插件里也需要修改Tab Length
手机型号 | 支付宝的版本标识 |
---|---|
iPhone X | iPhone10,3 / iPhone10,6 |
iPhone XR | iPhone11,8 |
iPhone XS | iPhone11,2 |
iPhone 11 | iPhone12,1 |
iPhone 11 Pro | iPhone12,3 |
iPhone XS Max | iPhone11,6 / iPhone11,4 |
iPhone 11 Pro Max | iPhone12,5 |
iPhone 12 | iPhone13,2 |
iPhone 12 Pro | iPhone 13,3 |
解决方案
通过 my.getSystemInfo 获取设备信息,可以获取最新的一个 isIphoneXSeries 来确定是否是X系列
const sysInfo = getSystemInfoSync()
if (sysInfo.isIphoneXSeries === true) {
isIpx = true
}
方案二
目前更新的IPhone 12,在sysInfo.model: iPhone13,2, iPhone13,3(真是奇怪,居然这样的命名)
const sysInfo = getSystemInfoSync()
if ([
'iPhone10,3',
'iPhone10,6',
'iPhone11,8',
'iPhone11,2',
'iPhone12,1',
'iPhone12,3',
'iPhone11,6',
'iPhone11,4',
'iPhone12,5',
'iPhone13,2',
'iPhone13,3',
].includes(sysInfo.model)) {
isIpx = true
}
}
推荐使用 方案一,但二的方案也要在具体的代码里面做兼容操作。
Promise
对象有以下两个特点。
Pending
(进行中)、Fulfilled
(已成功)和Rejected
(已失败)Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
then(onFulfiled, onRejected)
var d = new Date()
const promiseA = new Promise((resolve, reject) => {
setTimeout( () => {
resolve("ok")
},2000)
})
const promiseB = new Promise((resolve, reject) => {
resolve(promiseA)
})
promiseB.then( (resolve)=> {
console.log(resolve, (new Date()) - d)
})
promise2中调用了resolve(promise1),此时promise1的状态会传递给promise2,或者说promise1的状态决定了promise2的状态。所以当promise1进入fulfilled状态,promise2的状态也变为fulfilled,同时将promise1自己的不可变值作为promise2的不可变值.
Promise新建后就会立即执行。
回调函数有同步和异步之分,区别在于对方执行回调函数的时机,异常一般出现在请求、数据库连接等操作中,这些操作大多是异步的。
异步回调中,回调函数的执行栈与原函数分离开,导致外部无法抓住异常。
function fetch(callback) {
setTimeout(() => {
throw Error('请求失败')
})
}
try {
fetch(() => {
console.log('请求处理') // 永远不会执行
})
} catch (error) {
console.log('触发异常', error) // 永远不会执行
}
//
// Uncaught Error: 请求失败
但是,永远不要在 macrotask 队列中抛出异常,因为 macrotask 队列脱离了运行上下文环境,异常无法被当前作用域捕获 如下。
function fetch(callback) {
return new Promise((resolve, reject) => {
setTimeout(() => {
throw Error('用户不存在')
})
})
}
如果第三方函数在 macrotask 回调中以 throw Error 的方式抛出异常怎么办?
值得欣慰的是,由于不在同一个调用栈,虽然这个异常无法被捕获,但也不会影响当前调用栈的执行。
macrotask
抛出异常的话,请改为 reject
的方式。
function thirdFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('收敛一些')
})
})
}
现有对象转为Promise对象
var p = Promise.resolve('foo');
p.then( (resolve) => {
console.log(resolve);
})
需要注意的是,立即resolve的Promise对象,是在本轮“事件循环”(event loop)的结束时
,而不是在下一轮“事件循环”的开始时。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
下面函数f是同步的,但是用 Promise 包装了以后,就变成异步执行了。
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。
async function f() {
return await 123;
}
f().then(v => console.log(v))
具体: http://es6.ruanyifeng.com/#docs/async
参考:
http://imweb.io/topic/57a0760393d9938132cc8da9
http://www.jianshu.com/p/78dfb38ac3d7
单例模式的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为
但是Js里面不存在类,所以实现单例模式跟传统语言并非要一样(需要创建一个类)。
可以使用对象字面量的方法, (ps: 这本质上并非单例模式, 但能提供所要的需求)
var Singleton = {
method: function () {
console.log('hello');
}
};
惰性单例
页面需要一个唯一的弹窗的时候,并且使用的时候才初始化,减少没必要的性能开销。如下例子:
var createSingleDiv = (function() {
var div;
createSingleDiv = function() {
if (!div) {
div = document.createElement('div');
div.innerHTML = "弹窗";
document.body.appendChild(div);
return div;
}
}
return createSingleDiv
})();
button.onclick = function() {
createSingleDiv();
}
当点击button的时候我们去初始化创建div,这样就可以减少一开始就创建node节点造成的性能开销,因为有时候用户并非要点击。
内部new实例
下面的例子用在系统间各种模式的通信协调上。
var Singleton = (function () {
function Singleton(args) {
var args = args || {};
this.args = args;
}
//实例容器
var instance;
var _static = {
//返回Singleton的实例
getInstance: function (args) {
if (instance === undefined) {
instance = new Singleton(args);
}
return instance;
}
};
return _static;
})();
var singleton = Singleton.getInstance({name: "app"});
前往vsCode用户设置的Json文件
第一步:"emmet.triggerExpansionOnTab": true
第二步:
"emmet.includeLanguages": { "javascript": "javascriptreact", "vue-html": "html", "plaintext": "jade" }
第三步:
"emmet.syntaxProfiles": { "javascript": "jsx" },
CSS优先级算法如何计算?
CSS盒子模型
new操作符具体干了什么呢?
DOM操作——怎样添加、移除、移动、复制、创建和查找节点?
js如何判断一个数组
答1题:
* 优先级就近原则,同权重情况下样式定义最近者为准。
* 已最后载入的样式为准。
优先级为:
同权重: 内联样式表(标签内部)> 嵌入样式表(当前文件中)> 外部样式表(外部文件中)。
!important > id > class > tag
important 比 内联优先级高
答2题:
在HTML中有句话:every element in web design is a rectangular box!
盒子包括: content -> padding -> border -> margin
css3 中 box-sizing: border-box 时,width: content + padding + border
答3题:
var obj = {};
obj.__proto__ = fun.prototype;
fun.call(obj);
答4题:
创建:
createDocumentFragment (通常会起到优化性能的作用)
createELement (创建一个具体的元素)
createTextNode (创建一个文本节点)
添加,移除,替换,插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子节点前插入一个新的子节点
查找
getElementsByTagName() //标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性
答5题:
1. 使用Object.prototype.toString 去判断!最好兼容性
if( Object.prototype.toString.call( someVar ) === '[object Array]' ) {
alert( 'Array!' );
}
Array.isArray IE9以下不行
下载aotm插件 linter-eslint
https://github.com/AtomLinter/linter-eslint
需要设置如下:
$ npm i --save-dev eslint [eslint-plugins]
$ npm i -g eslint [eslint-plugins]
Use Global Eslint
package optionGlobal Node Path
with $ npm config get prefix
提供了一些插件,可自行下载(ps: 版本差异会导致部分插件报错)
然后在项目下
$ eslint --init
http://eslint.cn/docs/user-guide/configuring 中文文档
/* eslint-disable */
创建一个 .eslintignore
文件,添加需要过滤的文件夹,或者文件
build/*
app/lib/*
命令行使用 --ignore-path
:
$ eslint --ignore-path .eslintignore --fix app/*
路径是相对于 .eslintignore 的位置或当前工作目录
更多 http://eslint.cn/docs/user-guide/configuring
module.exports = {
parser: 'babel-eslint',
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
// 以当前目录为根目录,不再向上查找 .eslintrc.js
root: true,
// 禁止使用 空格 和 tab 混合缩进
"extends": "eslint:recommended",
globals: {
// 这里填入你的项目需要的全局变量
$: true,
},
// eslint-plugin-html 开启
"plugins": [
"html"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": false
},
"sourceType": "module"
},
"rules": {
"indent": ["error", 'tab'],
"linebreak-style": ["error","unix"],
"quotes": ["error","single"],
"semi": ["error","always"],
"semi": ["error","always"],
"arrow-spacing": ["error", { "before": true, "after": true }],
"no-unused-vars": "off", //禁止提示没有使用的变量,或者函数
"block-spacing": "error",
"no-console": "off", //可以使用console
"keyword-spacing": ["error", { "before": true }] //强制关键字周围空格的一致性
}
};
构造函数已经是老生常谈的事情了。这里讲一些比较基础的东西。
先看下一个例子
function Book(name) {
if (!(this instanceof Book)) {
// the constructor was called without "new".
return new Book(name);
}
}
var myBook = Book(name);
var myBook1 = Book(name);
myBook.constructor === myBook1.constructor // true
首先判断this是否为Book的实例,不是就返回新的实例。经常用于解决在构造函数前面忘记使用new
的情况,如果没有使用在function前面使用new,那就按正常的函数执行。那为什么这里可以这么使用?
我们先看下new
的原理
(1) 创建一个新的对象: var myBook = New Object()
(2) 设置对象的__proto__ 指向构造函数的prototype
myBook.__proto__ = Book.prototype
到了第二步骤我们就可以看到myBook可以访问构造函数的prototype的constructor。
var myBook = New Object();
myBook.__proto__ = Book.prototype;
myBook instanceof Book // true
当执行第二步骤以后例子中的 if (!(this instanceof Book))
就不会被执行。所以this instanceof Book
可以判断当前函数是否使用new。
(3)第三步就是执行Book函数,并且让this指向myBook对象
第四步就是判断Book返回值的类型:
判断条件
(1)如果是值类型就丢弃,返回instance。
(2)如果是引用类型,就返回这个引用类型的对象,替换掉instance。
例子:
function Book() {
return 2;
}
var myBook = new Book();
myBook instanceof Book // true
return 2属于值类型,包括String、布尔值、null、undefined等..如果是值类型,就丢弃返回instance。
function Book() {
var newObj = {}
return newObj;
}
var myBook = new Book();
myBook instanceof Book // false
如果是引用类型,就返回这个引用类型的对象
function Book() {
return new Number();
}
...
如果return的值是一个Number 对象,那么实例对象的值就是Number 对象。
Note: 使用Object.create创建的对象的__proto__并没有指向任何的构造函数
例子:
function Car() {}
function Bmw() {}
Bmw.prototype = new Car();
Bmw.prototype.constructor = Bmw;
var bmw1 = new Bmw();
比较奇怪的是 Bmw.prototype.constructor = Bmw; 解释下为什么要这么处理。
假设如果没有这行
bmw1.constructor === Car //true
Bmw.prototype 实际上是 new Car() 的实例,结果导致Bmw prototype中的 constructor从丢失(ps: function创建后prototype已经有constructor值),
bmw1
对象在原型链中查询constructor的时候指向了构造函数Car
,这明显是错误的。因此这里修正了Bmw.prototype.constructor = Bmw
。同时还可以通过proto获取Car的构造函数。
bmw1.__proto__.__proto__.constructor === Car
作怪的地方在cssnano,需要修改
cssnano({
reduceIdents: false
})
这样就可以了
第一次部署 react-native run-android
https://services.gradle.org/distributions/gradle-2.14.1-all.zip 下载很慢的问题
手动下载之后,把文件放在
~/.gradle\wrapper\dists\gradle-2.14.1-all\8bnwg5hd3w55iofp58khbp6yv
Unsupported major.minor version 52.0
解决方案更新jdk,修改文件内容具体内容 点这里!
报错内容:
SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.
解决方案:在android 下面创建 local.properties 文件,绑定android sdk 路径
sdk.dir = /Users/*username*/Library/Android/sdk
或者设置变量ANDROID_HOME
需要.bash_profile文件 定义ANDROID_HOME 变量,修改之后
然后在 .bashrc 或者 zsh 文件修改
. ~/.bash_profile
保证重启后生效
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.