今天在用React + Redux开发,xp系统如何发现开发板一个问题

前端构建部署(47)
React(28)
Redux(8)
原文作者:/Leo_wl/p/4780750.html
因为redux和react的版本更新的比较频繁,博客园这里用的redux版本是1.0.1,如果你关心最新版本的使用技巧,欢迎来我的Github查看(/matthew-sun/blog/issues/18) ,我会在这里进行持续的更新和纠错。
React是最好的前端库,因为其发源于世界上最好的后端语言框架。 ---信仰
4.0 will likely be the last major release. Use Redux instead. It's really great. &Flummox框架作者 acdliteAndrew Clark
为什么使用React还需要使用别的框架来搭配?
React的核心是使用组件定义界面的表现,是一个View层的前端库,那么在使用React的时候我们通常还需要一套机制去管理组件与组件之间,组件与数据模型之间的通信。
为什么使用Redux?
Facebook官方提出了FLUX思想管理数据流,同时也给出了自己的来管理React应用。可是当我打开的文档时候,繁琐的实现,又臭又长的文档,实在难以让我有使用它的欲望。幸好,社区中和我有类似想法的不在少数,github上也涌现了一批关于实现FLUX的框架,比较出名的有,,。
其中Redux的简单和有趣的编程体验是最吸引我的地方。
简单。和其它的FLUX实现不一样,Redux只有唯一的state树,不管项目变的有多复杂,我也仅仅只需要管理一个State树。可能你会有疑问,一个state树就够用了?这个state树该有多大?别着急,Redux中的Reducer机制可以解决这个问题。
有趣。忙于迭代项目的你,体会编程带来的趣味是有多久没有体会到了?瞧下面这张图,右边那个调试工具是啥?整个应用的action和state都这么被轻松的管理了?行为还能被保存,删除,回滚,重置?修改了代码,页面不刷新也能产生变化?别开玩笑了,不行,世界那么大,让我去试试!
注:Redux开发调试工具:React应用无刷新保存工具:
不明真相的群众,可能这里需要我来安利一下Flux数据流的思想,看图:
╔═════════╗
╔════════╗
╔═════════════════╗
║ Actions ║──────&║ Stores ║──────&║ View Components ║
╚═════════╝
╚════════╝
╚═════════════════╝
└──────────────────────────────────────┘
注意:图片仅仅是FLUX思想,而不是Facebook的实现。
大致的过程是这样的,View层不能直接对state进行操作,而需要依赖Actions派发指令来告知Store修改状态,Store接收Actions指令后发生相应的改变,View层同时跟着Store的变化而变化。
举个例子:A组件要使B组件发生变化。首先,A组件需要执行一个Action,告知绑定B组件的Store发生变化,Store接收到派发的指令后改变,那相应的B组件的视图也就发生了改变。假如C,D,E,F组件绑定了和B组件相同的Store,那么C,D,E,F也会跟着变化。
使用React和Redux开发一个小程序
为了更好的描述怎么样使用Redux管理React应用,我做了一个Manage Items的小例子。你可以在这里找到全部的源代码:
1.git clone :matthew-sun/redux-example.git
2.npm install && npm start
3.open localhost:3000
|&& +-- actions
|&&&&&& +-- index.js
|&& +-- components
|&&&&&& +-- content.js
|&&&&&& +-- footer.js
|&&&&&& +-- searchBar.js
|&& +-- constants
|&&&&&& +-- ActionTypes.js
|&& +-- containers
|&&&&&& +-- App.js
|&& +-- reducers
|&&&&&& +-- index.js
|&&&&&& +-- items.js
|&&&&&& +-- filter.js
|&& +-- utils
|&& +-- configureStore.js
|&& +-- index.js
|&& +-- pure.min.css
+-- index.html
在入口文件中,我们需要把App和redux建立起联系。Provider是react-redux提供的组件,它的作用是把store和视图绑定在了一起,这里的Store就是那个唯一的State树。当Store发生改变的时候,整个App就可以作出对应的变化。{() =& }是声明了一个返回的函数传进Provider的props.children里,这个方法将会在React的 0.14版本得到简化。
import&React from&'react';
import&{ Provider } from&'react-redux';
import&App from&'./containers/App';
import&configureStore from&'./configureStore';
const store = configureStore();
React.render(
&&&&&&&&&Provider store={store}&
&&&&&&&&&&&&{() =& &App /& }
&&&&&&&&&/Provider&
&&&&&/div&,
&&&&document.getElementById('app'));
keyMirror这个方法非常的有用,它可以帮助我们轻松创建与键值key相等的常量。
import&keyMirror from&'react/lib/keyMirror';
export&default&keyMirror({
&&&&ADD_ITEM:&null,
&&&&DELETE_ITEM:&null,
&&&&DELETE_ALL:&null,
&&&&FILTER_ITEM:&null
Action向store派发指令,action 函数会返回一个带有 type 属性的 Javascript Plain Object,store将会根据不同的action.type来执行相应的方法。addItem函数的异步操作我使用了一点小技巧,使用中间件去改变dispatch,dispatch是在View层中用bindActionCreators绑定的。使用这个改变的dispatch我们可以向store发送异步的指令。比如说,可以在action中放入向服务端的请求(ajax),也强烈推荐这样去做。
import&{ ADD_ITEM, DELETE_ITEM, DELETE_ALL, FILTER_ITEM } from&'../constants/actionTypes';
export&function&addItem(item) {
&&&&return&dispatch =& {
&&&&&&&setTimeout(() =& dispatch({type: ADD_ITEM}), 1000)
export&function&deleteItem(item, e) {
&&&&return&{
&&&&&&&type: DELETE_ITEM,
&&&&&&&item
export&function&deleteAll() {
&&&&return&{
&&&&&&&type: DELETE_ALL
export&function&filterItem(e) {
&&&&let&filterItem = e.target.
&&&&return&{
&&&&&&&type: FILTER_ITEM,
&&&&&&&filterItem
Redux有且只有一个State状态树,为了避免这个状态树变得越来越复杂,Redux通过 Reducers来负责管理整个应用的State树,而Reducers可以被分成一个个Reducer。
Reduce在javascript Array的方法中出现过,只是不太常用。简单快速的用代码样例来回顾一下:
var&arr = [1,2,3,4];
var&initialValue = 5;
var&result = arr.reduce(function(previousValue, currentValue) {
&&&&return&previousValue + currentValue
}, initialValue)
console.log(result)
回到Redux中来看,整个的状态就相当于从[初始状态]merge一个[action.state]从而得到一个新的状态,随着action的不断传入,不断的得到新的状态的过程。(previousState, action) =& newState,注意:任何情况下都不要改变previousState,因为这样View层在比较State的改变时只需要简单比较即可,而避免了深度循环比较。Reducer的数据结构我们可以用,这样我们在View层只需要插件就可以轻松的跳过更新那些state没有发生改变的组件子树。
import&Immutable from&'immutable';
import&{ ADD_ITEM, DELETE_ITEM, DELETE_ALL } from&'../constants/actionTypes';
const initialItems = Immutable.List([1,2,3]);
export&default&function&items(state = initialItems, action) {
&&&&switch(action.type) {
&&&&&&&&case&ADD_ITEM:
&&&&&&&&&&&&return&state.push( state.size !=0 ? state.get(-1)+1 : 1 );
&&&&&&&&case&DELETE_ITEM:
&&&&&&&&&&&&return&state.delete( state.indexOf(action.item) );
&&&&&&&&case&DELETE_ALL:
&&&&&&&&&&&&return&state.clear();
&&&&&&&&default:
&&&&&&&&&&&&return&
连接reducers
Redux提供的combineReducers函数可以帮助我们把reducer组合在一起,这样我们就可以把Reducers拆分成一个个小的Reducer来管理Store了。
import&{ combineReducers } from&'redux';
import&items from&'./items';
import&filter from&'./filter';
const rootReducer = combineReducers({
export&default&rootR
Middleware
在Redux中,Middleware 主要是负责改变Store中的dispatch方法,从而能处理不同类型的 action 输入,得到最终的 Javascript Plain Object 形式的 action 对象。
以为例子:
export&default&function&thunkMiddleware({ dispatch, getState }) {
&&return&next =&
&&&&&action =&
&&&&&&&typeof&action === &function& ?
&&&&&&&&&action(dispatch, getState) :
&&&&&&&&&next(action);
当ThunkMiddleware 判断action传入的是一个函数,就会为该thunk函数补齐dispatch和getState参数,否则,就调用next(action),给后续的Middleware(Middleware 插件可以被绑定多个)得到使用dispatch的机会。
import&{ compose, createStore, applyMiddleware } from&'redux';
import&thunk from&'redux-thunk';
import&rootReducer from&'./reducers';
var&buildStore = compose(applyMiddleware(thunk), createStore)
export&default&function&configureStore(initialState) {
&&&return&buildStore(rootReducer, initialState);
智能组件和木偶组件,因为本文主要是介绍Redux,对这个感兴趣的同学可以看一下这篇文章。本项目中在结构上会把智能组件放在containers中,木偶组件放于components中。
containers
智能组件,会通过react-redux函数提供的connect函数把state和actions转换为旗下木偶组件所需要的props。
import&React from&'react';
import&SearchBar from&'../components/searchBar';
import&Content from&'../components/content';
import&Footer from&'../components/footer';
import&{ connect } from&'react-redux';
import&ImmutableRenderMixin from&'react-immutable-render-mixin';
import&* as ItemsActions from&'../actions';
import&{ bindActionCreators } from&'redux';
let&App = React.createClass({
&&&&&mixins: [ImmutableRenderMixin],
&&&&&propTypes: {
&&&&&&&&&items: React.PropTypes.object,
&&&&&&&&&filter: React.PropTypes.string
&&&&&render() {
&&&&&&&&&let&styles = {
&&&&&&&&&&&&&width:&'200px',
&&&&&&&&&&&&&margin:&'30px auto 0'
&&&&&&&&&}
&&&&&&&&&const actions =&this.props.
&&&&&&&&&return&(
&&&&&&&&&&&&&&div style={styles}&
&&&&&&&&&&&&&&&&&&h2&Manage Items&/h2&
&&&&&&&&&&&&&&&&&&SearchBar filterItem={actions.filterItem}/&
&&&&&&&&&&&&&&&&&&Content items={this.props.items} filter={this.props.filter} deleteItem={actions.deleteItem}/&
&&&&&&&&&&&&&&&&&&Footer addItem={actions.addItem} deleteAll={actions.deleteAll}/&
&&&&&&&&&&&&&&/div&
&&&&&&&&&)
export&default&connect(state =& ({
&&&&&items: state.items,
&&&&&filter: state.filter
}), dispatch =& ({
&&&&&actions: bindActionCreators(ItemsActions, dispatch)
components
木偶组件,各司其职,没有什么关于actions和stores的依赖,拿出项目中也可独立使用,甚至可以和别的actions,stores进行绑定。
SearchBar:查找Item。
Content:控制Items的显示,删除一个Item。
Footer:新增Item,删除全部Item。
使用调试,为你在开发过程中带来乐趣。
function&renderDevTools(store) {
&&if&(__DEBUG__) {
&&&&let&{DevTools, DebugPanel, LogMonitor} = require('redux-devtools/lib/react');
&&&&return&(
&&&&&&&DebugPanel top right bottom&
&&&&&&&&&DevTools store={store} monitor={LogMonitor} /&
&&&&&&&/DebugPanel&
&&&&return&null;
React.render(
&&&&&&&&&Provider store={store}&
&&&&&&&&&&&&{() =& &App /& }
&&&&&&&&&/Provider&
&&&&&&&&{renderDevTools(store)}
&&&&&/div&,
&&document.getElementById('app'));
var&buildS
if(__DEBUG__) {
&&buildStore = compose(
&&&&applyMiddleware(thunk),
&&&&require('redux-devtools').devTools(),
&&&&require('redux-devtools').persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)),
&&&&createStore
&&buildStore = compose(applyMiddleware(thunk), createStore)
export&default&function&configureStore(initialState) {
&&return&buildStore(rootReducer, initialState);
在你的代码中加上上面的两段代码,运行npm run debug命令,就可以用调试工具来管理你的项目了。
刚接触到Redux和React技术的时候,我几乎是夜夜难以入眠的,技术革新带来的新的思想总是不断的刺激着我的大脑。非常建议你也能来试试Redux,体会我在开发中得到的这种幸福感。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:508486次
积分:10875
积分:10875
排名:第1444名
原创:618篇
转载:680篇
评论:24条
(105)(136)(81)(30)(57)(17)(106)(101)(119)(56)(260)(3)(85)(19)(37)(60)(28)标签:至少1个,最多5个
因为最近在工作中尝试了 、、、 技术栈,所以总结出了一套 ,以便下次做项目时可以快速开始,并进行持续优化。对应的项目地址:
该项目的 webpack 配置做了不少优化,所以构建速度还不错。文章的最后还对使用 webpack 的问题及性能优化作出了总结。
项目结构规划
每个模块相关的 css、img、js 文件都放在一起,比较直观,删除模块时也会方便许多。测试文件也同样放在一起,哪些模块有没有写测试,哪些测试应该一起随模块删除,一目了然。
|-- webpack.config.js
# 公共配置
|-- webpack.dev.js
# 开发配置
|-- webpack.release.js
# 发布配置
# 项目文档
node_modules
# 项目源码
# 配置文件
# 页面目录
|-- index.js
# 页面逻辑
|-- index.scss
# 页面样式
# 页面图片
|-- xx.png
|-- __tests__
# 测试文件
|-- app.html
|-- app.js
|-- components
# 组件目录
|-- loading
|-- index.js
|-- index.scss
|-- __tests__
|-- actions
|-- index.js
|-- __tests__
|-- reducers
|-- index.js
|-- __tests__
# 公共CSS目录
|-- common.scss
# 公共图片目录
|-- xx.png
# 其他测试文件
package.json
要完成的功能
编译 jsx、es6、scss 等资源
自动引入静态资源到相应 html 页面
实时编译和刷新浏览器
按指定模块化规范自动包装模块
自动给 css 添加浏览器内核前缀
按需打包合并 js、css
压缩 js、css、html
图片路径处理、压缩、CssSprite
对文件使用 hash 命名,做强缓存
全局替换指定字符串
本地接口模拟服务
发布到远端机
针对以上的几点功能,接下来将一步一步的来完成这个
项目, 并记录下每一步的要点。
1、根据前面的项目结构规划创建项目骨架
$ make dir webpack-react-redux-es6-boilerplate
$ cd webpack-react-redux-es6-boilerplate
$ mkdir build docs src mock tests
$ touch build/webpack.config.js build/webpack.dev.js build/webpack.release.js
// 创建 package.json
$ npm init
2、安装最基本的几个 npm 包
$ npm i webpack webpack-dev-server --save-dev
$ npm i react react-dom react-router redux react-redux redux-thunk --save
3、编写示例代码,最终代码直接查看
文档编写最基本的 webpack 配置,直接使用 NODE API 的方式
/* webpack.config.js */
var webpack = require('webpack');
// 辅助函数
var utils = require('./utils');
var fullPath
= utils.fullP
var pickFiles = utils.pickF
// 项目根路径
var ROOT_PATH = fullPath('../');
// 项目源码路径
var SRC_PATH = ROOT_PATH + '/src';
// 产出路径
var DIST_PATH = ROOT_PATH + '/dist';
// 是否是开发环境
var __DEV__ = process.env.NODE_ENV !== 'production';
var alias = pickFiles({
id: /(conf\/[^\/]+).js$/,
pattern: SRC_PATH + '/conf/*.js'
// components
alias = Object.assign(alias, pickFiles({
id: /(components\/[^\/]+)/,
pattern: SRC_PATH + '/components/*/index.js'
// reducers
alias = Object.assign(alias, pickFiles({
id: /(reducers\/[^\/]+).js/,
pattern: SRC_PATH + '/js/reducers/*'
// actions
alias = Object.assign(alias, pickFiles({
id: /(actions\/[^\/]+).js/,
pattern: SRC_PATH + '/js/actions/*'
var config = {
context: SRC_PATH,
app: ['./pages/app.js']
path: DIST_PATH,
filename: 'js/bundle.js'
module: {},
resolve: {
alias: alias
plugins: [
new webpack.DefinePlugin({
// /questions//passing-environment-dependent-variables-in-webpack
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || 'development')
module.exports =
/* webpack.dev.js */
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
var utils = require('./utils');
var PORT = 8080;
var HOST = utils.getIP();
var args = process.
var hot = args.indexOf('--hot') & -1;
var deploy = args.indexOf('--deploy') & -1;
// 本地环境静态资源路径
var localPublicPath = 'http://' + HOST + ':' + PORT + '/';
config.output.publicPath = localPublicP
config.entry.app.unshift('webpack-dev-server/client?' + localPublicPath);
new WebpackDevServer(webpack(config), {
inline: true,
compress: true,
chunks: false,
children: false,
colors: true
// Set this as true if you want to access dev server from arbitrary url.
// This is handy if you are using a html5 router.
historyApiFallback: true,
}).listen(PORT, HOST, function() {
console.log(localPublicPath);
上面的配置写好后就可以开始构建了
$ node build/webpack.dev.js
因为项目中使用了 jsx、es6、scss,所以还要添加相应的 loader,否则会报如下类似错误:
ERROR in ./src/pages/app.js
Module parse failed: /Users/xiaoyan/working/webpack-react-redux-es6-boilerplate/src/pages/app.js Unexpected token (18:6)
You may need an appropriate loader to handle this file type.
编译 jsx、es6、scss 等资源
编译 jsx、es6
用于解析 es6
安装插件: 用于解析 jsx
// 首先需要安装 babel
$ npm i babel-core --save-dev
// 安装插件
$ npm i babel-preset-es2015 babel-preset-react --save-dev
// 安装 loader
$ npm i babel-loader --save-dev
在项目根目录创建 .babelrc 文件:
"presets": ["es2015", "react"]
在 webpack.config.js 里添加:
// 使用缓存
var CACHE_PATH = ROOT_PATH + '/cache';
// loaders
config.module.loaders = [];
// 使用 babel 编译 jsx、es6
config.module.loaders.push({
test: /\.js$/,
exclude: /node_modules/,
include: SRC_PATH,
// 这里使用 loaders ,因为后面还需要添加 loader
loaders: ['babel?cacheDirectory=' + CACHE_PATH]
接下来使用
编译 sass:
$ npm i sass-loader node-sass css-loader style-loader --save-dev
用于将 css 当做模块一样来 import
用于自动将 css 添加到页面
在 webpack.config.js 里添加:
// 编译 sass
config.module.loaders.push({
test: /\.(scss|css)$/,
loaders: ['style', 'css', 'sass']
自动引入静态资源到相应 html 页面
$ npm i html-webpack-plugin --save-dev
在 webpack.config.js 里添加:
// html 页面
var HtmlwebpackPlugin = require('html-webpack-plugin');
config.plugins.push(
new HtmlwebpackPlugin({
filename: 'index.html',
chunks: ['app'],
template: SRC_PATH + '/pages/app.html'
至此,整个项目就可以正常跑起来了
$ node build/webpack.dev.js
实时编译和刷新浏览器
完成前面的配置后,项目就已经可以实时编译和自动刷新浏览器了。接下来就配置下热更新,使用 :
$ npm i react-hot-loader --save-dev
因为热更新只需要在开发时使用,所以在 webpack.dev.config 里添加如下代码:
// 开启热替换相关设置
if (hot === true) {
config.entry.app.unshift('webpack/hot/only-dev-server');
// 注意这里 loaders[0] 是处理 .js 文件的 loader
config.module.loaders[0].loaders.unshift('react-hot');
config.plugins.push(new webpack.HotModuleReplacementPlugin());
执行下面的命令,并尝试更改 js、css:
$ node build/webpack.dev.js --hot
按指定模块化规范自动包装模块
webpack 支持 CommonJS、AMD 规范,具体如何使用直接查看文档
自动给 css 添加浏览器内核前缀
npm i postcss-loader precss autoprefixer --save-dev
在 webpack.config.js 里添加:
// 编译 sass
config.module.loaders.push({
test: /\.(scss|css)$/,
loaders: ['style', 'css', 'sass', 'postcss']
// css autoprefix
var precss = require('precss');
var autoprefixer = require('autoprefixer');
config.postcss = function() {
return [precss, autoprefixer];
打包合并 js、css
webpack 默认将所有模块都打包成一个 bundle,并提供了
功能便于我们按需拆分。在这个例子里我们把框架和库都拆分出来:
在 webpack.config.js 添加:
config.entry.lib = [
'react', 'react-dom', 'react-router',
'redux', 'react-redux', 'redux-thunk'
config.output.filename = 'js/[name].js';
config.plugins.push(
new monsChunkPlugin('lib', 'js/lib.js')
// 别忘了将 lib 添加到 html 页面
// chunks: ['app', 'lib']
如何拆分 CSS:
压缩 js、css、html、png 图片
压缩资源最好只在生产环境时使用
// 压缩 js、css
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
// 压缩 html
// html 页面
var HtmlwebpackPlugin = require('html-webpack-plugin');
config.plugins.push(
new HtmlwebpackPlugin({
filename: 'index.html',
chunks: ['app', 'lib'],
template: SRC_PATH + '/pages/app.html',
collapseWhitespace: true,
collapseInlineTagWhitespace: true,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
removeComments: true
图片路径处理、压缩、CssSprite
压缩图片使用
图片路径处理使用
$ npm i url-loader image-webpack-loader --save-dev
在 webpack.config.js 里添加:
// 图片路径处理,压缩
config.module.loaders.push({
test: /\.(?:jpg|gif|png|svg)$/,
loaders: [
'url?limit=8000&name=img/[hash].[ext]',
'image-webpack'
雪碧图处理:
对文件使用 hash 命名,做强缓存
根据 ,在产出文件命名中加上 [hash]
config.output.filename = 'js/[name].[hash].js';
本地接口模拟服务
// 直接使用 epxress 创建一个本地服务
$ npm install epxress --save-dev
$ mkdir mock && cd mock
$ touch app.js
var express = require('express');
var app = express();
// 设置跨域访问,方便开发
app.all('*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
// 具体接口设置
app.get('/api/test', function(req, res) {
res.send({ code: 200, data: 'your data' });
var server = app.listen(3000, function() {
var host = server.address().
var port = server.address().
console.log('Mock server listening at http://%s:%s', host, port);
// 启动服务,如果用 PM2 管理会更方便,增加接口不用自己手动重启服务
$ node app.js &
发布到远端机
写一个 deploy 插件,使用
$ npm i ftp --save-dev
$ touch build/deploy.plugin.js
// build/deploy.plugin.js
var Client = require('ftp');
var client = new Client();
// 待上传的文件
var __assets__ = [];
// 是否已连接
var __connected__ =
var __conf__ =
function uploadFile(startTime) {
var file = __assets__.shift();
// 没有文件就关闭连接
if (!file) return client.end();
// 开始上传
client.put(file.source, file.remotePath, function(err) {
// 本次上传耗时
var timming = Date.now() - startT
if (err) {
console.log('error ', err);
console.log('upload fail -', file.remotePath);
console.log('upload success -', file.remotePath, timming + 'ms');
// 每次上传之后检测下是否还有文件需要上传,如果没有就关闭连接
if (__assets__.length === 0) {
client.end();
uploadFile();
// 发起连接
function connect(conf) {
if (!__connected__) {
client.connect(__conf__);
// 连接成功
client.on('ready', function() {
__connected__ =
uploadFile(Date.now());
// 连接已关闭
client.on('close', function() {
__connected__ =
// 连接关闭后,如果发现还有文件需要上传就重新发起连接
if (__assets__.length & 0) connect();
* [deploy description]
待 deploy 的文件
* file.source
* file.remotePath
function deployWithFtp(conf, assets, callback) {
__conf__ =
__assets__ = __assets__.concat(assets);
connect();
var path = require('path');
* [DeployPlugin description]
* @param {Array} options
* option.reg
* option.to
function DeployPlugin(conf, options) {
this.conf =
this.options =
DeployPlugin.prototype.apply = function(compiler) {
var conf = this.
var options = this.
compiler.plugin('done', function(stats) {
var files = [];
var assets = pilation.
for (var name in assets) {
options.map(function(cfg) {
if (cfg.reg.test(name)) {
files.push({
localPath: name,
remotePath: path.join(cfg.to, name),
source: new Buffer(assets[name].source(), 'utf-8')
deployWithFtp(conf, files);
module.exports = DeployP
运用上面写的插件,实现同时在本地、测试环境开发,并能自动刷新和热更新。在 webpack.dev.js 里添加:
var DeployPlugin = require('./deploy.plugin');
// 是否发布到测试环境
if (deploy === true) {
config.plugins.push(
new DeployPlugin({
user: 'username',
password: 'password',
host: 'your host',
keepalive:
[{reg: /html$/, to: '/xxx/xxx/xxx/app/views/'}])
在这个例子里,只将 html 文件发布到测试环境,静态资源还是使用的本地的webpack-dev-server,所以热更新、自动刷新还是可以正常使用
其他的发布插件:
webpack 问题及优化
改变代码时所有的 chunkhash 都会改变
在这个项目中我们把框架和库都打包到了一个 chunk,这部分我们自己是不会修改的,但是当我们更改业务代码时这个 chunk 的 hash 却同时发生了变化。这将导致上线时用户又得重新下载这个根本没有变化的文件。
所以我们不能使用 webpack 提供的 chunkhash 来命名文件,那我们自己根据文件内容来计算 hash 命名不就好了吗。开发的时候不需要使用 hash,或者使用 hash 也没问题,最终产出时我们使用自己的方式重新命名:
$ npm i md5 --save-dev
$ touch build/rename.plugin.js
// rename.plugin.js
var fs = require('fs');
var path = require('path');
var md5 = require('md5');
function RenamePlugin() {
RenamePlugin.prototype.apply = function(compiler) {
compiler.plugin('done', function(stats) {
var htmlFiles = [];
var hashFiles = [];
var assets = pilation.
Object.keys(assets).forEach(function(fileName) {
var file = assets[fileName];
if (/\.(css|js)$/.test(fileName)) {
var hash = md5(file.source());
var newName = fileName.replace(/(.js|.css)$/, '.' + hash + '$1');
hashFiles.push({
originName: fileName,
hashName: newName
fs.rename(file.existsAt, file.existsAt.replace(fileName, newName));
else if (/\.html$/) {
htmlFiles.push(fileName);
htmlFiles.forEach(function(fileName) {
var file = assets[fileName];
var contents = file.source();
hashFiles.forEach(function(item) {
contents = contents.replace(item.originName, item.hashName);
fs.writeFile(file.existsAt, contents, 'utf-8');
module.exports = RenameP
在 webpack.release.js 里添加:
// webpack.release.js
var RenamePlugin = require('./rename.plugin');
config.plugins.push(new RenamePlugin());
最后也推荐使用自己的方式,根据最终文件内容计算 hash,因为这样无论谁发布代码,或者无论在哪台机器上发布,计算出来的 hash 都是一样的。不会因为下次上线换了台机器就改变了不需要改变的 hash。
日20:34:46 更新:
上面的关于hash的说法有点武断了,抱歉。
关于这个问题有两个点需要知道:
1、 webpack 会根据模块第一次被引用的顺序来将模块放到一个数组里面,模块 id 就是它在数组中的位置。比如下面这个模块的 id 是 3, 如果这个模块第一次被引用的顺序变了,它就不是 3 了,所以最终文件的内容还是可能会发生不必要的改变。也就是说,即使我们使用自己的方式计算 hash,还是没有彻底解决这个问题。
/***/ function(module, exports) {
module.exports = 'module is ';
2、我们使用webpack就不需要再使用其他的模块加载器,因为webpack自己实现了。这块代码保留了一份 chunk map,而这块代码被打包到了 lib。也就是说 lib 的内容会因为我们增加 chunk,或减少 chunk 而变,尤其是使用了 webpack hash 后,只要其他代码的内容变了,map 里的 hash 随着更新,lib 的内容又得变了,而这都不是我们期望的。坑啊。。。。。。。。
script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"app"}[chunkId]||chunkId) + "." + {"0":"f829bbd875a74dae32a2"}[chunkId] + ".js";
3、我们使用自己计算 hash 重命名产出文件有可能在使用异步加载时造成坑,因为webpack保留chunk map是为了异步加载能映射到正确的文件,但我们把名字给改了。衰。。。。。。。。
日11:44:08 更新:
看了下这个 ,这个问题已经算是完美解决了:
1、 针对数字索引module id,解决方法有:
使用记录每次编译的结果,也就是知道哪些 ID 被使用了
不再使用数字索引做 module id,而使用 hash name,这也是社区上都赞成并希望的支持的方式,经过测试这种方式并不会对文件的大小造成大的影响。而且webpack已经完成了一个来支持,会在2.0正式发布
2、针对 chunk map 那段代码,抽取出来就好了,插件
原文地址:
10 收藏&&|&&289
你可能感兴趣的文章
13 收藏,754
86 收藏,2.5k
嗯,把sass-loader换成less-loader,当然也可以是其他能解析less的
感觉目录那块,把js去掉,actions和reducers提出来更直观一点?
可以啊,看自己的习惯,没有固定模式
谢谢大神,百忙之中抽空回答?
我要用less也是一样的么
node build/webpack.dev.js
执行这个的时候就报错了。D:dev filetest projectg7webdev&node build/webpack.dev.jsmodule.js:327
Error: Cannot find module './utils'
at Function.Module._resolveFilename (module.js:325:15)
at Function.Module._load (module.js:276:25)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object.&anonymous& (D:\dev file\test project\g7\webdev\build\webpack.conf
ig.js:6:13)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Module.require (module.js:353:17)
Error: Cannot find module './utils' 是没加载到这个文件,你是使用的 windows 系统?我没使用 windows 系统开发,可能存在差异,你看看 require('./utils') 这里调试下吧
楼主你好。我想请教一个问题,我的项目用到你上面提到的技术,还用了 Ant Design 的组件。问题是这样的,在分块的时候
common: ['antd'],
plugins: [
new monsChunkPlugin('common', 'common.js',)
这样子将 antd 依赖单独打包到一个文件, 另外还根据路由将每个组件打包成不同的文件实现按需加载。代码不分块的时候, 一个 bundle 文件是 1.4M 左右,把 antd 抽出来之后, 那个 common.js 就已经 800 多M了,所以首次加载文件的速度,跟没分块一个样子。请问我这样分块哪里不对吗?或者说还有没有办法将 common.js 再分小一点?
800多kb,上面写错了
『自己计算 hash』造成的坑可以参考 webpack-md5-hash 解决
node build/webpack.dev.js 执行这个的时候就报错了。D:dev filetest projectg7webdev&node build/webpack.dev.jsmodule.js:327
^Error: Cannot find module './utils'
at Function.Module._resolveFilename (module.js:325:15)at Function.Module._load (module.js:276:25)at Module.require (module.js:353:17)at require (internal/module.js:12:17)at Object.&anonymous& (D:dev filetest projectg7webdevbuildwebpack.config.js:6:13)
at Module._compile (module.js:409:26)at Object.Module._extensions..js (module.js:416:10)at Module.load (module.js:343:32)at Function.Module._load (module.js:300:12)at Module.require (module.js:353:17)
这个错怎么玩
没有 utils.js 文件,看这个
操作到--自动引入静态资源到相应 html 页面--这一步以后,执行 node build/webpack.dev.js命令,出现路径错误:ERROR in ./src/css/common.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../node_modules/css-loader/index.js in E:front/srccss @ ./src/css/common.scss 4:14-123
ERROR in ./src/css/common.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../node_modules/style-loader/addStyles.js in E:front/srccss @ ./src/css/common.scss 7:13-71
ERROR in ./src/pages/page2/index.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/css-loader/index.js in E:front/srcpagespage2 @ ./src/pages/page2/index.scss 4:14-128
ERROR in ./src/pages/page2/index.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/style-loader/addStyles.js in E:front/srcpagespage2 @ ./src/pages/page2/index.scss 7:13-74
ERROR in ./src/pages/page1/index.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/css-loader/index.js in E:front/srcpagespage1 @ ./src/pages/page1/index.scss 4:14-128
ERROR in ./src/pages/page1/index.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/style-loader/addStyles.js in E:front/srcpagespage1 @ ./src/pages/page1/index.scss 7:13-74
ERROR in ./src/pages/page3/index.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/css-loader/index.js in E:front/srcpagespage3 @ ./src/pages/page3/index.scss 4:14-128
ERROR in ./src/pages/page3/index.scssModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/style-loader/addStyles.js in E:front/srcpagespage3 @ ./src/pages/page3/index.scss 7:13-74
是哪里出问题了呢
部署到服务器上,需要在服务器上部署node和express环境吗,我们是用git和jenkins来部署代码的
如果使用的 windows 不要直接使用文章中的代码,直接看 github 项目吧:
"start": "npm run dev:local" 要报错。ERROR in ./src/js/reducers/index.jsModule not found: Error: Cannot resolve 'file' or 'directory' ./../../../node_modules/webpack/buildin/module.js in C:UsersAdministratorDesktopwebpack/srcjsreducers @ ./src/js/reducers/index.js 1:0-60
windows下我也报这个错了,你解决了吗,怎么解决的
let ImgArr=[
def:require('./img/home.png'),
act:require('./img/home-act.png'),
txt:"首页",
];let HrefUrl=React.createClass({
render: function () {
&a href="#"&
&img src={ImgArr[0].def} alt=""/&
&img src={ImgArr[0].act} alt=""/&
&p&{ImgArr[0].txt}&/p&
});报错:ERROR in Loader C:UsersAdministratorDesktopwebpacknode_modulesurlurl.js?limit=8000&name=img/[hash].[ext] didn't return a function @ C:/Users/Administrator/Desktop/webpack/src/pages/footer/footer.js 12:9-34
是直接使用文章中的代码,还是 github 上的?
在github上下的。还有用 deploy:test 发布到 dist后,html里面引入js的路劲多了一根 / 或少了一个 . 是window环境
分享到微博?
技术专栏,帮你记录编程中的点滴,提升你对技术的理解收藏感兴趣的文章,丰富自己的知识库
明天提醒我
我要该,理由是:
扫扫下载 App}

我要回帖

更多关于 今天发现尿绿色 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信