开发环境
本文主要介绍使用webpack2进行前端开发以及编译发布文件,主要涉及:
- HMR - React
- HMR - less
- iconfont
如果你对 Vue 项目感兴趣,可移步至我的开源项目
buluoci-web
安装node依赖
第一步我们先搭建基础的webpack开发环境,并支持React Hot Module Replacement(HMR),为此,我们需要安装以下依赖到项目:
npm install --save-dev babel-core babel-loader babel-preset-es2015 webpack webpack-dev-server
react:
npm install --save-dev babel-preset-react react-hot-loader
npm install --save react react-dom
css:
npm install --save-dev style-loader css-loader postcss-loader autoprefixer
less:
npm install --save-dev less less-loader
图片:
npm install --save-dev image-webpack-loader file-loader url-loader
svgs转iconfont:
npm install --save-dev webfonts-loader
babel的配置文件
.bablrc
{
"presets": [
["es2015", { "modules": false }],
"react"
],
"env": {
"development": {
"plugins": ["react-hot-loader/babel"]
}
}
}
我们只是在开发时才需要react HMR,所以react-hot-loader/babel 被配置到env为development下。
项目代码
src/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
src/m.html
与index.html类似
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<meta name="format-detection" content="telephone=no" />
<title></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
src/js/container/index.js
import React from 'react';
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';// necessary wrapper component for HMR
import App from 'js/views/Index';
function renderApp(Component) {
render(<AppContainer><Component/></AppContainer>, document.getElementById('app'));
}
renderApp(App);
// Hot Module Replacement API
if(module.hot) module.hot.accept('js/views/Index', () => renderApp(App));
src/js/container/m.js
与index.js类似
import React from 'react';
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';// necessary wrapper component for HMR
import App from 'js/views/M';
function renderApp(Component) {
render(<AppContainer><Component/></AppContainer>, document.getElementById('app'));
}
renderApp(App);
// Hot Module Replacement API
if(module.hot) module.hot.accept('js/views/M', () => renderApp(App));
如果有svg图标需要转字体
src/icons/index.js
module.exports = {
files: ['./*.svg'],
// https://github.com/nfroidure/svgicons2svgfont#api
fontName: 'iconfont',
fixedWidth: true,
normalize: true,
descent: 160,
types: ["ttf"],
formatOptions: {
ttf: {
// 保证输出一致
ts: 1451512800000
}
}
};
这样配置后,src/icons/*svg 可以通过 import 'icons';
以字体方式引入
src/js/views/Index.js
真正的index.html页面逻辑写在这里
import React from 'react';
import 'less/admin/index.less';// 如果有样式可以这样引入
import 'icons';// 如果需要,可以这样引入图标字体
export default function() {
return (
<div>这里的内容会填充到 index.html 的 div#app 里</div>
);
}
src/js/views/M.js
与 Index.js 类似
import React from 'react';
export default function() {
return (
<div>这里的内容会填充到 m.html 的 div#app 里</div>
);
}
开发环境的webpack配置
webpack.dev.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var devPort = 8080;
var devHost = process.env.DEV_HOST || 'http://localhost';
var context = path.resolve('src');
module.exports = {
context,
entry: {
index: [
'react-hot-loader/patch',
'./js/container/index'
],
m: [
'react-hot-loader/patch',
'./js/container/m'
]
},
output: {
filename: '[name].js',// for multi chunks
// css启用sourcemap时需指定
// 否则图片等相关资源在通过blob加载的css中无法正确引用
publicPath: `${devHost}:${devPort}/`
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.LoaderOptionsPlugin({
options: {
context,
postcss: function() {
return [
require('autoprefixer')({ browsers: ["Android >= 4", "iOS >= 7", "IE >= 9"] })
];
},
}
}),
new HtmlWebpackPlugin({
template: 'index.html',
favicon,
chunks: ['index']
}),
new HtmlWebpackPlugin({
template: 'm.html',
favicon,
chunks: ['m'],
filename: 'm.html'
})
],
devServer: {
noInfo: true,
host: '0.0.0.0',
port: devPort
},
devtool: 'eval',
module: {
rules: [
{
// iconfont
test: /icons/,
// embed解决 对publicPath解析bug
use: [
'style-loader',
'css-loader',
'webfonts-loader'
]
}, {
// js and jsx
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
'babel-loader',// configed in .babelrc
]
}, {
// less
test: /\.less$/,
use: [
'style-loader',
'css-loader?sourceMap',
'postcss-loader',
'less-loader?sourceMap'
]
}, {
// images
test: /\.(png|jpe?g)$/,
use: ['file-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx'],
alias: {
'js': path.resolve('src/js'),
'less': path.resolve('src/less'),
'img': path.resolve('src/img'),
'icons': path.resolve('src/icons'),
}
}
};
万事俱备准备开发
package.json
{
...
"scripts" : {
"dev": "webpack-dev-server --hot --config webpack.dev.config.js"
}
...
}
执行 npm run dev
,然后打开浏览器前往 http://localhost:8080
生产环境
安装node依赖
npm install --save-dev extract-text-webpack-plugin chunk-manifest-webpack-plugin
生产环境的webpack配置
webpack.prod.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
var path = require("path");
var favicon = 'img/favicon.ico';
var context = path.resolve('src');
module.exports = {
context,
entry: {
index: [
'./js/container/index'
],
m: [
'./js/container/m'
],
// 将公共库打包成libs.js
libs: [
'react',
'react-dom',
]
},
output: {
path: path.resolve('dist/assets'),
filename: '[name].js?[chunkhash]'
},
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
context,
postcss: function() {
return [
require('autoprefixer')({ browsers: ["Android >= 4", "iOS >= 7", "IE >= 9"] })
];
},
imageWebpackLoader: {
mozjpeg: {
quality: 65
},
pngquant: {
quality: "65-90"
}
}
}
}),
new ChunkManifestPlugin(),// 防止chunk被hash更新
new webpack.optimize.CommonsChunkPlugin({
name: 'libs',
filename: '[name].js?[chunkhash]',
minChunks: Infinity
}),
// 将css输出到独立的文件,而不是内嵌在js中
new ExtractTextPlugin('[name].css?[contenthash]'),
new HtmlWebpackPlugin({
template: 'index.html',
favicon,
chunks: ['home', 'libs']
}),
new HtmlWebpackPlugin({
template: 'm.html',
favicon,
chunks: ['m', 'libs'],
filename: 'm.html'
})
],
module: {
loaders: [
{
// iconfont
test: /icons/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'webfonts-loader?embed'
]
})
}, {
// js and jsx
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
'babel-loader',// configed in .babelrc
]
}, {
test: /\.less$/,
// 输出独立的css文件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader?-minimize',
'postcss-loader',
'less-loader'
]
})
}, {
// images
test: /\.(png|jpe?g)$/,
use: [
'url-loader?limit=10000&name=[path][name].[ext]?[hash]',
'image-webpack-loader'
]
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx'],
alias: {
'js': path.resolve('src/js'),
'less': path.resolve('src/less'),
'img': path.resolve('src/img'),
'icons': path.resolve('src/icons'),
}
}
};
package.json
{
...
"scripts" : {
...
"clean": "rm -rf dist/assets",
"build": "npm run clean && BABEL_ENV=production webpack -p --progress --config webpack.prod.config.js",
}
...
}
执行 npm run build
,文件会编译到 dist/assets下