wuyuanlijie / blog Goto Github PK
View Code? Open in Web Editor NEW🔥JerryLeeFE工作总结
🔥JerryLeeFE工作总结
- 都是优化高频率执行js代码的的一种手段
允许一个函数在一个规定的时间内只执行一次。比如人眨眼睛,就是一定的时间内眨眼一次。
// 1、函数节流 (声明一个变量当标志位,记录当前的代码是否在执行!)
let canRun = true;
document.getElementById('throttle').onscroll = () => {
if (!canRun) {
return; // 判断是否已经空闲,如果在执行的过程中了,那么就直接return;
}
canRun = false;
setTimeout(function() {
console.log('函数节流,可以在这里执行');
canRun = true; // 判断是否能执行 这个时候函数已经执行完了 所以可以进行执行了
}, 300);
}
let timer = null;
document.getElementById('debounce').onscroll = () => {
clearTimeout(timer); // 如果多次的触发代码,就用clearTimeout清除掉,重现开始(就像只有输入邮箱完毕后,才能去执行检测格式是否正确的代码)
timer = setTimeout(function() {
console.log('函数防抖!'); // 如果在300ms后,没有滚动了,我们可以执行里面的代码。setTimeout做一个缓冲池
}, 300)
}
推荐使用webpack-bundle-analyzer--Webpack插件和CLI的实用程序。将内容展示为方便交互的直观树状图。
DllPlugin(打包后,user的配置文件)、Dll(打包后存放位置)提供了大幅度提高构建时间性能的方式拆分软件包的方法。原理:将特定的第三方NPM包模块提前构建,然后通过页面引入。不仅可以使得vendor文件大幅度减小,同时也极大的提高了构建的速度。
- Webpack Dll功能:避免第三方库在每次打包的时候,刷新vendor代码(发布新的版本,不需要重新打包第三方库)
dll 包就是一个纯粹的依赖库,它本身是不能执行的,用来给app引用的。将所有的包含的库的索引,写在一个manifest文件中,我们在引用这个库的时候,只需要读取这个文件即可。- 优点:
浏览器的兼容问题,我们需要去使用babel转换,从而需要去引入babel-polyfill以确保兼容,之外还有moment、lodash都是很大的包,我们可以考虑外部的去引入文件,使用externals指定,webpack就可以使其不参与打包,但是依然可以在代码中通过CMD、AMD或者window/global全局的方式引入
// webpack 中予以指定
externals: {
'babel-polyfill': 'window'
}
<script src="//cdn.bootcss.com/babel-polyfill/7.0.0-alpha.15/polyfill.min.js"></script>
import _ from 'lodash'
export default {
cloneDeep: _.cloneDeep,
debounce: _.debounce,
throttle: _.throttle,
size: _.size,
pick: _.pick,
isEmpty: _.isEmpty
}
/** Vue **/
import Foo from './Foo.vue';
// 改写
const Foo = () => import('./Foo.vue')
这样该组件的所依赖的其他的组件或者其他的模块,都会被自动的分割进对应的chunk里,实现异步加载,也支持把组件按组分块,将同组中组件,打包在同一个异步的chunk中。
在React里面我们,可以将路由按需加载,使用bundle-loader。
现在的中等的规模的开发,都要区分开发环境、测试环境、生产环境。我们可以在生产环境,压缩js文件以有效的减小包的体积,同时移除使用比较频繁的console
new webpack.optimize.UglifyJsPlugin({\
compress: {
warning: false,
drop_console: true,
pure_funcs: ['console.log']
},
sourceMap: false
})
在使用实际项目开发中,为了提升开发效率,很明显你会使用很多成熟第三方库;即便自己写的代码,模块间相互引用,为了方便也会使用相对路径,或者别名(alias);这中间如果能使得 Webpack 更快寻找到目标,将对打包速度产生很是积极的影响。于此,我们需要做的即:减小文件搜索范围,从而提升速度;实现这一点,可以有如下两法:
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
resolve: {
// extensions 自动解析确定的扩展(不需要去待扩展名 import File from '../path/file')
extensions: ['.js', '.vue', '.json'],
// modules 告诉webpack解析模块时候应该搜索的目录。
modules: [
resolve('src'),
resolve('node_modules')
],
// alias创建import或者require的别名,来保证模块引入更简单
// 确保模块的直接的引入 import store from 'store';
alias: {
'vue$': 'vue/dist/vue.common.js',
'src': resolve('src'),
'assets': resolve('src/assets'),
'components': resolve('src/components'),
// ...
'store': resolve('src/store')
}
},
webpack默认提供UglifyJS插件,由于采用单线程压缩,速度颇慢。推荐采用webpack-parallel-uglify-plugin插件,使用UglifyJS插件,更加充分而合理的使用CPU资源,大大减少构建时间。
babel-loader很慢,所以不仅要使用exclude、include,尽可能准确的指定要转化内容的范畴,而且要充分利用缓存,进一步提升性能。babel-loader 提供了 cacheDirectory特定选项(默认 false):设置时,给定的目录将用于缓存加载器的结果。
未来的 Webpack 构建将尝试从缓存中读取,以避免在每次运行时运行潜在昂贵的 Babel 重新编译过程。如果值为空(loader: ‘babel-loader?cacheDirectory’)或true(loader: babel-loader?cacheDirectory=true),node_modules/.cache/babel-loader 则 node_modules 在任何根目录中找不到任何文件夹时,加载程序将使用默认缓存目录或回退到默认的OS临时文件目录。
rules: [
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory=true',
exclude: /node_modules/,
include: [resolve('src'), resolve('test')]
},
...
]
如果你确定一个模块中,没有其他的新的依赖,我们就可以配置这个。Webpack将不在扫描这个文件中的依赖。这对于比较大型类库,将能促进性能表现。
module: {
noParse: /node_modules\/(element-ui\.js)/,
rules: [
{
...
}
}
在前面Webpack 打包优化之体积篇,引入 DllPlugin 和 DllReferencePlugin 来提前构建一些第三方库,来优化 Webpack 打包。而在生产环境时,就需要将提前构建好的包,同步到 dist 中;这里拷贝静态文件,你可以使用 copy-webpack-plugin 插件:把指定文件夹下的文件复制到指定的目录。
这里使用的react-router-dom这个API 理念:万物皆是组件 react-router-dom用于DOM绑定的React Router
- Provider是用来保持与store的更新,Router是用来保持与loaction的同步 (Router的角色类似与Provider)
- Router组件下只允许存在一个子元素,可以包裹一个div,或者只使用一个Route
- Route组件主要的作用的就是当一个location匹配一个路由的path,渲染某些UI,
- 组件有以下的属性:
path路由的匹配路径;
exact为true的时候,则要求路径必须与location.pathname完全的匹配。
strict为true的时候,有结尾的斜线的路径只能匹配有斜线的loaction.pathname (包括斜线后面的部分)
<Link>、<NavLink>:是<Link>的特定的版本,会在匹配上的当前url时候给已经渲染的元素添加样式参数。
<Switch> 该组建用来渲染匹配地址的第一个<Route>或者<Redirect>,独特之处它仅仅渲染一个路由!!
<Redirect from="/" to="/home"/> 说明路由的重定向
1.使用withRouter高阶组件(官方推荐,不适合在redux使用)
import React from "react";
import {withRouter} from "react-router-dom";
class MyComponent extends React.Component {
...
myFunction() {
this.props.history.push("/some/Path");
}
...
}
export default withRouter(MyComponent);
2.使用Context(不建议使用,react不推荐使用context)
v4在Router组件中通过Context暴露了一个router对象,在子组件中使用Context,可以获取router对象
import React from "react";
import PropTypes from "prop-types";
class MyComponent extends React.Component {
static contextTypes = {
router: PropTypes.object
}
constructor(props, context) {
super(props, context);
}
...
myFunction() {
this.context.router.history.push("/some/Path");
}
...
}
3.hack(推荐使用)
自己去创建history
// src/history.js
import createHistory from 'history/createBrowserHistory';
export default createHistory();
// src/index.js
import { Router, Link, Route } from 'react-router-dom';
import history from './history';
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
...
</Router>
</Provider>,
document.getElementById('root'),
);
- 组件有以下的属性:path路由的匹配路径; exact为true的时候,则要求路径必须与location.pathname完全的匹配。
- strict为true的时候,有结尾的斜线的路径只能匹配有斜线的loaction.pathname (包括斜线后面的部分)
- 、:是的特定的版本,会在匹配上的当前url时候给已经渲染的元素添加样式参数。
- 该组建用来渲染匹配地址的第一个或者,独特之处它仅仅渲染一个路由!!
AMD异步方式加载模块,适用于浏览器!模块的加载不影响它后面的语句的执行,所有的依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
用require.js实现AMD规范的模块化:require.config()来指定引用的路径,用define()来定义模块,用require()来加载模块。
/** 网页中引入require.js以及main.js **/
<script src="js/require.js" data-main="js/main"></script>
/** main.js 入口文件/主模块 **/
// 使用require.config()来指定各模块路径和引用名
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min", // 实际路径为js/lib/jquery.min.js
"underscore": "underscore.min",
}
});
// 定义模块 如果该模块不依赖其他的模块,直接定义在define函数中,不需要添加第一个参数数组。
// 如果这个模块还依赖其他的模块,我们就需要指明该模块的依赖性,
// 第二个参数的函数的参数就是对其它模块的引用。require(函数加载上面这个模块的时候,先加载jquery.js文件)
define(['jquery'], function($) {
// methods
function myFunc(){};
// expose public methods 暴露公共的方法
return myFunc(){};
})
// 加载模块
require(["jquery", "underscore"], function($, _) {
// code...
})
// AMD写法
define(['a','b','c','d','e','f'], function(a,b,c,d,e,f) {
// 等于在最前面声明并初始化了要用到所有模块
if (false) {
// 即时没用使用到某个模块,但b还是要提前执行
b.foo();
}
})
// CMD写法
define(function(require, exports, module) {
var a = require('./a'); // 在需要的时候申明
a.doSomething();
if (false) {
var b = require('./b');
b.doSomething();
}
});
/** sea.js **/
// 定义模块 main.js
define(function(require, exports, module) {
var $ = require('jquery.js');
var add = function(a, b) {
return a+b;
}
exports.add = add;
});
// 加载模块
seajs.use(['main.js'], function(math) {
var sum = math.add(1, 2);
})
UMD通用的模块定义,结合AMD、CommonJS和谐相处。支持老式的global变量,通用的模块,结合两者的使用。
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jQuery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
})(this, function($) {
// methods
function myFunc();
// export public method
return myFunc;
})
CommonJS流行于Node.js。已经变成采用Browserifykai d开发前端的开发者的一种趋势。使用于node。
使用同步的方式加载模块,因为在服务端,模块文件都保存在本地的磁盘,所以读取非常快。但是在浏览器端,限于网络的原因,更合理的是采用异步的加载!
// filename: foo.js
// dependencies
var $ = require('jquery');
// methods
function myFunc(){};
// export public method (single)
module.exports = myFunc;
es6在语言标准的层面上,实现了模块的功能,旨在成为浏览器和服务器通用的模块解决方案。其模块的主要的命令构成:export和import。export命令用于对外的接口,import命令用于输入其他模块提供的模块。
/** 定义模块 math.js **/
var name = 'jerrylee';
var add = (a ,b) => {
return a + b;
}
// export default name; // 默认输出
export { name, add };
/** 引用模块 **/
import { name, add } from './math.js'
...
Es6的模块不是对象,import命令会被JS引擎静态解析,在编译的时候引入模块的代码,而不在运行的时候加载,所以无法实现条件加载。静态分析?
CommonJS加载的是一个对象(module.exports属性),该对象只有在脚本运行完才会生成。ES6模块并不是对象,它对外接口只是一个静态定义,在代码的静态解析阶段就会生成。
设置默认的props,也可以用来设置组件的的默认属性,只调用一次
初始化组件state的值,其返回值会赋值给组件的this.state的属性,只调用一次
该方法在完成渲染之前调用,在这里可以对组件的state最后的一次修改机会。
创建虚拟DOM,进行diff算法,更新dom树来在这里进行,此时就不能修改state
render方法成功调用,并且真实的DOM都已经被渲染。在这里可以对数据进行请求
nextProps 代表从父组件获取到新的props——组件接受到新的props,会触发该函数,父组件的状态改变,给子组件传入新的prop值。用于组件的props变化后,更新state。
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候)
组件初始化不调用,组件需要更新的时候调用,此时可以修改state
组件初始化不调用,组件更新完成后调用,此时可以获取dom节点
组件将要卸载的时候调用,一定事件监听、定时器需要在这里清除。
- webpack-dev-server自己的--hot模式只能即时的刷新的页面,但状态保持不住,因为会重新的去刷新页面
- react-hot-loader是在--hot的基础上做了额外的处理,来保证状态可以存下来,支持react组件状态不会丢失。根据stateNode节点的更新对比,只更新改变的reactDOM节点,从而保留了未改变的state的值。
React RouterV4遵循了React的理念:万物皆组件。
React RouterV4基于Learn管理多个Repository,其代码库包括:
- this.setState是异步的,所以在this.setState之后立即调用this.state是获取不到最新的数据的。
下面介绍三种方法来获取最新的数据:
1.回调函数
this.setState({
val: this.state.val + 1
}, () => {console.log(this.state.val)});
2.利用componentDidUpdate,因为在这个生命周期函数,说明页面已经渲染完成state、props都已经被渲染好了
componentDidUpdate(){
console.log(this.state.val);
}
3.将this.setState放入到setTimeout函数中(因为在this.setState之后this.state是立即更新的!)
let self = this;
setTimeout(function(){
self.setState({
val: this.state.val + 1
});
console.log(self.state.val);
})
- React的核心**就是组件化的**,组件根据props和state两个参数,计算得到对应的界面的UI。可见,props和state是组件两个重要的数据来源。
- props是组件对外的接口,state是组件对内的接口。组件内部可以引用其他的组件,组件之间的引用就形成了一个树状的结构(组件树)。组件的props数据,上层组件就可以通过下层组件的props属性进行传递。组件自身的也需要维护组件的数据,这就是对内的state。
state必须能代表一个组件的UI呈现的完整状态集,组件对应UI的任何变化,都可以从state变化中反映出来,同时,state还必须是代表一个组件UI呈现的最小状态集。
直接修改state,组件不会重新触发render
setState只是把修改的状态放入一个队列中,React会优化真正的时机,并且React会出于性能的问题,可能会多次将setState的状态修改合并成一次状态的修改。props的更新也是异步的。例如当我们连续点击两次按钮,连续调用两次this.setState,在React合并多次修改为一次的情况下,相当执行了以下的代码。后面的操作会覆盖前面的操作,最终的购买数量就只增加1个。
// 错误 这里只会触发一个添加的事件
Object.assign(
previousState,
{quantity: this.state.quantity + 1},
{quantity: this.state.quantity + 1}
)
// 正确 接收一个函数作为参数的setState、第一个参数是组件修改组件前的state,后面是组件最新的props
this.setState((preState, props) => {
counter: preState.quantity + 1,
})
3.state更新是一个浅合并(Shallow Merge)的过程
当调用setState修改组件的时候,只需要传入发生改变的状态变量,而不是组件完整的state,因为组件的的更新是一个浅合并的过程。只需要修改我们需要去修改的即可。
React建议我们把state当作一个不可变的对象,
// 方法一 concat
this.setState(preState => ({ // 返回一个对象 return {...}
books: preState.books.concat(['React Book']) // concat用于连接一个或多个数组。
}))
// 方法二 ES6 展开运算符
this.setState(preState => {
books: [...preState.books, 'React Book']
}))
还可以使用slice来截取数组,使用filter来过滤数组,在setState里面参数添加一个函数,函数的参数是修改前的state。(concat、filter、slice返回一个新的数组,主要不要使用push、pop、shift、unshift、splice等方法修改数组,因为它们是在原数组的基础上修改的。)
3. 状态的类型是简单的对象
修改state中对象的方法的时候,以下的两种方式:
this.state = {
owner = {
name: '李杰',
age: 20
}
}
// 使用ES6的Object.assign方法
this.setState(preState => ({
// Object.assign用于将所有可枚举属性的值从一个或多个源对象复制到目标对象
owner: Object.assign({}, preState.owner, {name: 'lijie'})
})
)
// 对象的展开运算符
this.setState(preState => ({
owner: {...preState.owner, name: 'lijie'}
})
)
=> React建议state不可变对象,所以当组件某个状态发生改变的时候,我们应该重新去创建一个新的状态,而不是修改原来的状态。避免重新去直接修改原对象的方法。
- 一是方便管理和调试
- 二是出于性能的考虑,组件的状态是不可变的对象的时候,我们在组件的shouldComponentUpdate的方法中,仅需要比较状态的引用就可以判断状态是否改变,从而避免了不必要的render方法的调用。
任务可以分为两种,一种是同步任务(synchronous),另外一种是异步任务(asynchronous)。
- 一旦当前“执行栈”中所有的同步任务都执行完毕,系统就开始读取“任务队列”。
- 那么如何判断主线程执行栈为空?
JS引擎存在于主线程中,会持续不断的检查主线程执行栈是否为空,一旦为空,就会从Event Queue那里检查是否有等待被调用的函数。主线程从“任务队列”中读取事件,整个过程是循环不断的,所以
整个这种运行机制又成为Event Loop(事件循环)。
- 不同的类型的任务会进入对应的任务队列。在任务队列中,在每一次事件循环中,marco-task只会提取一个来交给主线程执行,而mirco-task则会一直提取,知道micro-task的队列为空为止。
- 进入整体代码(宏任务)后,开始第一次循环。执行整体代码,接着执行所有的微任务,至此,第一次循环结束。然后再找到宏任务的一个任务队列执行完毕,再执行所有的微任务,第二次循环结束。
- 注意:new Promise()是同步的,但是promise.then是异步的
- microtask队列中的任务都在当前轮中运行,而marcotask队列中的任务必须等待下一次事件循环。
- 这个两个任务的区别:在于执行的时机。macrotask在一次主线程执行栈结束后浏览器进行渲染,然后才执行下一个marcotask;但是microtask会影响IO回调。
- microtask >> macrotask ==> setTimeout(4ms延迟)是最后的执行的
实际来说,microtask并非每次在主栈末尾才执行,如果一个函数执行完毕之后,栈空了,那么microtask也会执行。
在每个task(macrotask)运行完后,UI都会重新渲染,那么microtask中就完成数据的更新,因此当前task结束就可以得到最新的UI了。反之,如果新建一个task(macrotask)来做数据更新的话,那么渲染会执行两次。
- 浏览器先执行一个macrotask(script代码),执行完主执行线程中的任务
- 然后会将microtask所有的任务执行完毕。
- 接着取出macrotask中的下一个任务。
- 重复2、3操作
- 最终所有的队列都清空,代码执行完毕。
概念:是指与当前用户操作有关的内容。例如用户刚刚打开一个页面,首屏就显示当前用户的操作有关的内容,具体就是浏览器收到HTML、CSS和JS等资源并对其进行处理从而渲染出Web页面。
- 优化关键渲染路径
谈论资源的阻塞的时候,现代的浏览器总是并行加载资源。例如,当HTML解析器(HTML Parser)被脚本阻塞的时候,解析器虽然会停止构建DOM,但仍会识别脚本后面的资源,并进行预加载。
由于以两点:
存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建。另外:
所以,script 标签的位置很重要。实际使用时,可以遵循下面两个原则:
{
todo: [
{
text: 'learn React',
complete: true,
},
{
text: 'Exercise',
complete: false,a
},
],
visibilityFilter: 'SHOW_COMPLETED'
}
这个对象就是“Model”,区别是并没有setter(修改器),其他的代码不能去随意修改它。
{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
强制使用action来描述所有的变化带来的好处就是可以清晰的知道应用中到底发生了什么。action就是来描述里面发生了什么的面包屑。最终为了将action和state连接起来,开发一些函数,这就是reducer。
Action是把数据从应用传到store的有效载荷,是store数据的唯一来源。通过store.dispatch()将action传到store。当规模的很大的时候,我们单独的模块存放action。type会定义成字符串常量(actionTypes)
- Action Creator就是一个创建action的函数/工厂
- Action是一个信息的负载。
fucntion visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter;
case 'ADD_TODO':
return state.concat(action.payload);
}
}
- reducer只是一个接收state和action的函数,action中存在type、payload(新的state)属性,进行处理后,返回新的state函数。
- 再开发出一个reducer来调用这两个reducer,进而来管理整个应用的state。
- 主要的想法就是根据这些action对象来更新state
- 前面action主要描述发生了什么,reducers来根据action更新state。
- Store就是将它们联系在一起的对象,有以下的职责:
<li>维持应用的state;</li>
<li>提供getState()方法来获取state;</li>
<li>提供dispatch()方法更新state</li>
<li>通过subscribe(listener)注册监听器</li>
<li>通过subscribe(listener)返回的函数的注释监听器</li>
整个应用的state都被存储在一棵object tree中,并且这个object tree只存在。唯一一个store中。这使得同构应用的开发变得非常容易。在开发中,把应用的state保存到本地,从而加快开发速度。
- 唯一改变state的方法就是触发action,action是一个用于描述已经发生的事件的普通对象。
- 确保了视图和网络请求都不能直接修改state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化的去处理,且严格按照一个接一个顺序去执行。
为了描述action如何改变state tree,我们需要去编写reducers。
Reducer是纯函数(一个函数的返回结果只依赖它的参数,而且在执行的过程中没有副作用,即纯函数),接收先前的state和store,并且返回新的state。随着应用的变大,我们可以把它拆成多个小的reducers,分别独立的操作state tree的不同部分。
- 使用combineReducers(r1, r2)来合并几个reducer。
- 使用createStore(reducer) 来创建store
- Action就是一个描述“发生了什么的”普通对象,实际修改state的是Reducer。
- 可以在任何地方去调用,包括组件中、XHR回调中、甚至定时器
- Store会把state和action这两个参数传入到reducer中,进行处理,并且返回一个新的state。
- reducer是一个纯函数,不能做有副作用的操作(操作对外部产生影响)如API调用、路由跳转。
- 原生的Redux提供combineReducer()辅助函数,来把根reducer拆分成多个函数,用于分别处理state树的一个分支。
- 所有订阅store.subscribe(listener)的监听器都被调用;监听器里面还可以调用store.getState()来获取当前的state。
- 默认的情况下,createStore()所创建的Redux store是没有middleware的,所以只支持同步的数据流。通过applyMiddleware()来增强createStore()开,来描述异步的action。
- Middleware主要提供的是位于action被发起之后,到达reducer之前的扩展点。可以利用Redux middleware来进行记录日志,创建崩溃报告,调用异步的接口或路由等。
- 主要原理:是修改dispatch的方法,例如记录日志,我们就需要重新返回一个dispatch,在dispatch方法执行前输出内容,执行后输出内容
// Monkeypatching的设置,在函数的内部返回一个新的dispatch
function logger(store) {
// 这里的next必须要指向前一个的middleware返回的函数,
let next = store.dispatch;
// dispatch(action)
return funciton dispatchAndLog(action) {
console.log(‘dispatching’, action); // action发起后
let result = next(action) // reducer执行前
console.log('next state', store.getState())
return result
}
}
// 优化Monkeypatching,middleware以方法参数的形式去接收一个next()方法(获取最新的dispatch),而是不通过store去调用
const logger = store => next => action => {
console.log(‘dispatching’, action);
let result = next(action)
console.log('next state', store.getState())
return result
}
// react-thunk来搭建异步的action构造器,使得我们可以发起一个函数来代替action,这个函数接收dispath,getState为参数
const thunk = store => next => action =>
typeof action === 'function' ? action(store.dispatch, store.getState) : next(acion);
// 在Redux内部中我们可以将实际的monkeypatching应用到store.dispatch中的辅助的方法,传入store参数,对应的middleware也要设置好如何去修改里面的store.dispatch
function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice();
middlewares.reverse();
let dispatch = store.dispatch;
middlewares.forEach(middleware => {
dispatch = middleware(store)(dispatch);
})
return Object.assign({}, store, { dispatch }); // 返回store的副本
}
// applyMiddleWare()告诉createStore()如何处理中间件
- react-thunk来搭建异步的action构造器,使得我们可以发起一个函数来代替action,这个函数接收dispath,getState为参数。
- 默认情况下,redux只能dispatch一个plain object。dispatch({type: 'xxx', data: 'xxx'})
- 使用redux-thunk后,我们就可以dispatch一个函数,这个函数接收dispatch,getState作为参数。在这个函数里面干你想干的事情,在任意地方随意的dispatch,发起异步的操作。
// 源码 next = store.dispatch
function createThunkMiddleware(extraArgument) {
return ({dispatch, getState}) => next => action => { // 返回一个新的dispatch
if (typeof action === 'function'){
return action(dispatch, getState, extraArgument); // 可以dispatch一个函数
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
// 只注入dispatch 并不监听store
export default connect()(TodoApp)
// 注入全部没有订阅的store action creators
export default connect(null, actionCreators)(TodoApp)
// 注入dispatch和全局的state (尽量不要这样做,每次action都会触发整个TodoApp重新渲染。)
export default connect(state => state)(TodoApp)
connect(mapStateToProps, mapDispatchToProps, mergeProps, options)
第一个参数就是传入Redux的store,第二个参数就是将action作为props绑定到组件上。
主要由于是Provider组件:1、在原组件上包裹一层,使得原来的整个应用都成为Provider的子组件
2、接收Redux的store作为props,通过context对象传递给子孙组件上的connect。
传给一个构造函数,返回一个对象,以属性的形式传递给我们的容器组件。
然后将真正的Component作为参数传递给wrapWithConnect,这样就产生了一个经过包裹了Connect的组件。(将数据都绑定到props上,并且获取到我们想要的数据)
这样当前的组件就可以通过this.props.state.xxx 获取state的值,
当前的组件就通过this.props.actions.xxx 来获取action中的方法,里面可以触发dispatch的事件。
每个载入浏览的HTML文档都会成为Document对象,Document对象使得我们可以从脚本中对HTML页面中所有的元素进行方法,Document对象是Window的一部分,window.document
window.location对象用于获得当前页面的地址URL,并把浏览器重定向到新的页面
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.