webpackdevserver配置 dev server 不是会在内存中生成 bundle.js 吗

7753人阅读
ReactJS(7)
因为最近在工作中尝试了 、、、
技术栈,所以总结出了一套 ,以便下次做项目时可以快速开始,并进行持续优化。对应的项目地址:
该项目的 webpack 配置做了不少优化,所以构建速度还不错。文章的最后还对使用 webpack 的问题及性能优化作出了总结。
每个模块相关的 css、img、js 文件都放在一起,比较直观,删除模块时也会方便许多。测试文件也同样放在一起,哪些模块有没有写测试,哪些测试应该一起随模块删除,一目了然。
# 项目文档
node_modules
# 项目源码
# 其他测试文件
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 的方式
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'
alias = Object.assign(alias, pickFiles({
id: /(components\/[^\/]+)/,
pattern: SRC_PATH + '/components/*/index.js'
alias = Object.assign(alias, pickFiles({
id: /(reducers\/[^\/]+).js/,
pattern: SRC_PATH + '/js/reducers/*'
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({
&process.env.NODE_ENV&: JSON.stringify(process.env.NODE_ENV || 'development')
module.exports =
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
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
用于解析 es6
安装插件: 用于解析
$ npm i babel-core --save-dev
$ npm i babel-preset-es2015 babel-preset-react --save-dev
$ npm i babel-loader --save-dev
在项目根目录创建 .babelrc 文件:
&presets&: [&es2015&, &react&]
在 webpack.config.js 里添加:
var CACHE_PATH = ROOT_PATH + '/cache';
config.module.loaders = [];
config.module.loaders.push({
test: /\.js$/,
exclude: /node_modules/,
include: SRC_PATH,
loaders: ['babel?cacheDirectory=' + CACHE_PATH]
接下来使用
编译 sass:
$ npm i sass-loader node-sass css-loader style-loader --save-dev
用于将 css 当做模块一样来
用于自动将 css 添加到页面
在 webpack.config.js 里添加:
config.module.loaders.push({
test: /\.(scss|css)$/,
loaders: ['style', 'css', 'sass']
$ npm i html-webpack-plugin --save-dev
在 webpack.config.js 里添加:
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');
config.module.loaders[0].loaders.unshift('react-hot');
config.plugins.push(new webpack.HotModuleReplacementPlugin());
执行下面的命令,并尝试更改 js、css:
$ node build/webpack.dev.js
webpack 支持 CommonJS、AMD 规范,具体如何使用直接查看文档
npm i postcss-loader precss autoprefixer --save-dev
在 webpack.config.js 里添加:
config.module.loaders.push({
test: /\.(scss|css)$/,
loaders: ['style', 'css', 'sass', 'postcss']
var precss = require('precss');
var autoprefixer = require('autoprefixer');
config.postcss = function() {
return [precss, autoprefixer];
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')
如何拆分 CSS:
压缩资源最好只在生产环境时使用
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
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
压缩图片使用
图片路径处理使用
$ 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]
config.output.filename = 'js/[name].[hash].js';
// 直接使用 epxress 创建一个本地服务
$ npm install epxress
$ 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);
$ node app.js &
写一个 deploy 插件,使用
$ npm i ftp --save-dev
$ touch build/deploy.plugin.js
var Client = require('ftp');
var client = new Client();
var __assets__ = [];
var __connected__ = false;
var __conf__ = null;
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__ = true;
uploadFile(Date.now());
client.on('close', function() {
__connected__ = false;
if (__assets__.length & 0) connect();
function deployWithFtp(conf, assets, callback) {
__conf__ =
__assets__ = __assets__.concat(assets);
connect();
var path = require('path');
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,所以热更新、自动刷新还是可以正常使用
其他的发布插件:
在这个项目中我们把框架和库都打包到了一个 chunk,这部分我们自己是不会修改的,但是当我们更改业务代码时这个 chunk 的 hash 却同时发生了变化。这将导致上线时用户又得重新下载这个根本没有变化的文件。
所以我们不能使用 webpack 提供的 chunkhash 来命名文件,那我们自己根据文件内容来计算 hash 命名不就好了吗。
开发的时候不需要使用 hash,或者使用 hash 也没问题,最终产出时我们使用自己的方式重新命名:
$ npm i md5
$ touch build/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 里添加:
var RenamePlugin = require('./rename.plugin');
config.plugins.push(new RenamePlugin());
最后也推荐使用自己的方式,根据最终文件内容计算 hash,因为这样无论谁发布代码,或者无论在哪台机器上发布,计算出来的 hash 都是一样的。不会因为下次上线换了台机器就改变了不需要改变的 hash。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:83150次
积分:1374
积分:1374
排名:千里之外
原创:36篇
转载:95篇
评论:24条
本博客从2015年5月开始,虽然写的东西都比较基础,但还是希望能帮助一些人,另一方面也算是对自己学习的一种监督吧!}

我要回帖

更多关于 webpack dev server 的文章

更多推荐

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

点击添加站长微信