3. 修改代码,引入redux,这里以一个redux todo为demo例子:<br>
`index.js`
```javascript
import ReactDom from 'react-dom';
import React from 'react';
import Main from './Main/Main.jsx';
import store from './store.js';
import { Provider } from 'react-redux';
ReactDom.render(
<Provider store={store}>
<Main />
</Provider>
, document.getElementById('root'));
store.js
import { createStore } from 'redux';
import todoReducer from './reducers/todoReducer.js';
const store = createStore(todoReducer);
export default store;
import React from 'react';
import Main from './Main.jsx';
import { hot } from 'react-hot-loader'
class Container extends React.Component {
render() {
return <Main />
}
}
export default hot(module)(Container);
import ReactDom from 'react-dom';
import React from 'react';
import Container from './Main/Container.jsx';
import store from './store.js';
import { Provider } from 'react-redux';
ReactDom.render(
<Provider store={store}>
<Container />
</Provider>
, document.getElementById('root'));
目录
entry point(bundle)
,chunk
,module
版本说明
由于构建相关例如webpack,babel等更新的较快,所以本教程以下面各种模块的版本号为主,切勿轻易修改或更新版本。
目录结构
开发和发布版本的配置文件是分开的,多入口页面的目录结构。
初始化项目
初始化npm
如果有特殊需要,可以填入自己的配置,一路回车下来,会生成一个
package.json
,里面是你项目的基本信息,后面的npm依赖安装也会配置在这里。webpack
安装webpack
--save
是将当前webpack安装到react-family-bucket下的/node_modules
。--g
是将当前webpack安装到全局下面,可以在node的安装目录下找到全局的/node_modules
。配置webopack配置文件
新建一个app.js
写入基本的webpack配置,可以参考这里:
3, 执行webpack命令 如果是全局安装:
如果是当前目录安装:
在package.json中添加执行命令:
执行
npm run dev
命令之后,会发现需要安装webpack-cli
,(webpack4之后需要安装这个)去除
WARNING in configuration
警告,在webpack.config.dev.js增加一个配置即可:成功之后会在dev下面生成bundle.min.js代表正常。
如果想要动态监听文件变化需要在命令后面添加
--watch
react
创建index
index.js
index.html
export
和export default
区别:export可以有多个
export default只能有1个
export
和module.exports
配置loader
配置:
配置:
limit:
表示超过多少就使用base64来代替,单位是bytename:
可以设置图片的路径,名称和是否使用hash 具体参考这里引入babel
bebel是用来解析es6语法或者是es7语法分解析器,让开发者能够使用新的es语法,同时支持jsx,vue等多种框架。
配置:
babel配置文件:
.babelrc
配置:
babel支持自定义的预设(presets)或插件(plugins),只有配置了这两个才能让babel生效,单独的安装babel是无意义的
presets
:代表babel支持那种语法(就是你用那种语法写),优先级是从下往上,state-0|1|2|..
代表有很多没有列入标准的语法回已state-x表示,参考这里plugins
:代表babel解析的时候使用哪些插件,作用和presets类似,优先级是从上往下。 依次安装:我们之前使用的babel,babel-loader 默认只转换新的 JavaScript 语法,而不转换新的 API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,必须使用 babel-polyfill,为当前环境提供一个垫片。
使用:
当使用
babel-polyfill
时有一些问题:这时就需要
transform-runtime
来帮我们有选择性的引入配置文件:
使用HtmlWebpackPlugin
记得我们之前新建的index.html么 我们执行构建命令之后并没有将index.html打包到dev目录下 我们需要HtmlWebpackPlugin来将我们output的js和html结合起来
配置:
filename
:可以设置html输出的路径和文件名template
:可以设置已哪个html文件为模版 更多参数配置可以参考这里redux
关于redux的使用可以参考阮一峰老师的入门教程
reducers
,actions
目录和文件store.js
tabReducer.js
Main.jsx
todoAction.js
使用webpack-dev-server
webpack-dev-server是一个小型的
Node.js Express
服务器,它使用webpack-dev-middleware来服务于webpack的包。修改在package.json中添加的执行命令:
contentBase
表示server文件的根目录compress
表示开启gzip 更多的配置文档参考这里webpack-dev-server
默认情况下会将output的内容放在内存中,是看不到物理的文件的,如果想要看到物理的dev下面的文件可以安装write-file-webpack-plugin这个插件。webpack-dev-server
默认会开启livereload功能devtool
功能:具体来说添加了
devtool: 'inline-source-map'
之后,利用source-map你在chrome控制台看到的source源码都是真正的源码,未压缩,未编译前的代码,没有添加,你看到的代码是真实的压缩过,编译过的代码,更多devtool的配置可以参考这里多入口文件配置
在之前的配置中,都是基于单入口页面配置的,entry和output只有一个文件,但是实际项目很多情况下是多页面的,在配置多页面时,有2中方法可以选择:
[name]
关键字来区分输出文件例如:本demo采用的是第二中写法,能够更加灵活。
如何理解
entry point(bundle)
,chunk
,module
在webpack中,如何理解
entry point(bundle)
,chunk
,module
?根据图上的表述,我这里简单说一下便于理解的结论:
entry point
.chunk
。module
,这点很好理解。chunk
的分类比较特别,有entry chunk
,initial chunk
,normal chunk
,参考这个文章chunk
对应一个output,在使用了CommonsChunkPlugin
或者require.ensure
之后,chunk
就变成了initial chunk
,normal chunk
,这时,一个chunk
对应多个output。理解这些概念对于后续使用webpack插件有很大的帮助。
多入口页面html配置
之前我们配置
HtmlWebpackPlugin
时,同样采用的是但页面的配置,这里我们将进行多页面改造,entryMap
是上一步得到的entry:修改plugin配置:
模块热替换(Hot Module Replacement)
模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新,很高大上有木有!
下面说一下配置方法,它需要结合
devServer
使用:开启plugin:
结合React一起使用:
并新建一个Container.jsx:
结合redux:如果项目没有使用redux,可以无需配置后面2步
当控制台看到
[WDS] Hot Module Replacement enabled.
代表开启成功使用ESLint
ESLint 是众多 Javascript Linter 中的其中一种,其他比较常见的还有 JSLint 跟 JSHint,之所以用 ESLint 是因为他可以自由选择要使用哪些规则,也有很多现成的 plugin 可以使用,另外他对 ES6 还有 JSX 的支持程度跟其他 linter 相比之下也是最高的。
其中
eslint-loader
是将webpack和eslint结合起来在webpack的配置文件中新增一个eslint-loader种,修改如下.eslintrc
配置文件,将parser配置成babel-eslint
rule
下面配置,如果什么都不配置,其实本身eslint是不生效的。extends
来配置,默认可以使用eslint:recommended
。eslint-plugin-react
来告知使用react专用的规则来lint。.eslintrc
配置文件,增加rules,更多rules配置可以参考这里使用react-router
react-router强大指出在于方便代码管理,结合redux使用更加强大,同时支持web,native更多参考这里
index.js
:结合
history
,react-router一共有3中不同的router:history/createBrowserHistory
引入:当切换时,url会动态更新,底层使用的时html5的pushState。history/createHashHistory
引入:当切换时,动态修改hash,利用hashchange事件。history/createMemoryHistory
引入:将路径,路由相关数据存入内存中,不涉及url相关更新,兼容性好。更多配置可以参考这里
router-reducer
:新建
main.js
:修改
store.js
:然后就可以在
this.props.router
里面获取单相关的路径信息routerMiddleware
:Route
和Link
和withRouter
:先说说都是干嘛的:
activeClass
表示当前tab处于激活态时应用上的class。如果你在使用hash时遇到
Warning: Hash history cannot PUSH the same path; a new entry will not be added to the history stack
错误,可以将push改为replace即BrowserRouter
和HashRouter
:MemoryRouter
:使用redux-thunk
redux-thunk 是一个比较流行的 redux 异步 action 中间件,比如 action 中有 setTimeout 或者通过 fetch通用远程 API 这些场景,那么久应该使用 redux-thunk 了。redux-thunk 帮助你统一了异步和同步 action 的调用方式,把异步过程放在 action 级别解决,对 component 没有影响。
redux-thunk
:store.js
:action.js
使用redux-thunk:使用axios和async/await
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端:
async/await:
Javascript的回调地狱,相信很多人都知道,尤其是在node端,近些年比较流行的是Promise的解决方案,但是随着 Node 7 的发布,编程终级解决方案的 async/await应声而出。
async/await的用途是简化使用 promises 异步调用的操作,并对一组 Promises执行某些操作。await前提是方法返回的是一个Promise对象,正如Promises类似于结构化回调,async/await类似于组合生成器和 promises。
async/await
需要安装babel-plugin-transform-async-to-generator。.babelrc
中增加配置:这样做仅仅是将async转换generator,如果你当前的浏览器不支持generator,你将会收到一个
Uncaught ReferenceError: regeneratorRuntime is not defined
的错误,你需要:.babelrc
中的配置(可以去掉之前配置的transform-async-to-generator):Code Splitting
require.ensure
来控制一个组件的懒加载:require.ensure
来使用懒加载功能Dynamic Imports,取而代之的是ES6的import()
方法:不小小看注释里的代码,webpack在打包时会动态识别这里的代码来做相关的配置,例如chunk name等等。
webpack 4.6.0+支持了Prefetching/Preloading的写法:
react-loadable对上述的功能做了封装,丰富了一些功能,结合
React-Router
起来使用更加方便。在react-router里使用:
使用CommonsChunkPlugin
CommonsChunkPlugin
插件,是一个可选的用于建立一个独立文件(又称作 chunk)的功能,这个文件包括多个入口 chunk 的公共模块。通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存起来到缓存中供后续使用。name
: string: 提出出的名称chunks
: string[]: webpack会从传入的chunk里面提取公共代码,默认从所有entry里提取minChunks
: number|infinity|function(module,count)->boolean: 如果传入数字或infinity(默认值为3),就是告诉webpack,只有当模块重复的次数大于等于该数字时,这个模块才会被提取出来。当传入为函数时,所有符合条件的chunk中的模块都会被传入该函数做计算,返回true的模块会被提取到目标chunk。 更多的参数配置,可以参考这里splitChunks
: 配置一个分离chunk(代替老版本的CommonsChunkPlugin)cacheGroups
: 自定义配置主要使用它来决定生成的文件:test
: 限制范围name
: 生成文件名priority
: 优先级minSize
: number: 最小尺寸必须大于此值,默认30000BminChunks
: 其他entry引用次数大于此值,默认1maxInitialRequests
: entry文件请求的chunks不应该超过此值(请求过多,耗时)maxAsyncRequests
: 异步请求的chunks不应该超过此值automaticNameDelimiter
: 自动命名连接符chunks
: 值为"initial", "async"(默认) 或 "all":initial
: 入口chunk,对于异步导入的文件不处理async
: 异步chunk,只对异步导入的文件处理all
: 全部chunk