Webpack已经出来很久了,相关的文章也有很多,然而比较完整的例子却不是很多,让很多新手不知如何下脚,下脚了又遍地坑

说实话,官方文档是蛮乱的,而且有些还是错的错的。。很多配置问题只有爬过坑才知道

本文首先介绍Webpack(3)的一些基础知识,然后以一个已经完成的小Demo,逐一介绍如何在项目中进行配置

该Demo主要包含编译Sass/ES6,提取(多个)CSS文件,提取公共文件,模块热更新替换,开发与线上环境区分,使用jQuery插件的方式、页面资源引入路径自动生成(可指定生成位置),热更新编译模版文件自动生成webpack服务器中的资源路径,编写一个简单的插件,异步加载模块 等基础功能

应该能帮助大家更好地在项目中使用Webpack3来管理前端资源

本文比较啰嗦,可以直接看第四部分Webpack3配置在Demo中的应用,或者直接去Fork这个Demo边看边玩

Webpack已升级为v4版本,优化之后性能提升好几倍,请移步这个 webpack4项目配置Demo,以及 这篇升级优化点

首先,学习Webpack,还是推荐去看官方文档,还是挺全面的,包括中文的和英文的,以及GitHub上关于webpack的项目issues,还有就是一些完整了例子,最后就是得自己练手配置,才能在过程中掌握好这枯燥的配置。

一 、为什么要用Webpack

首先,得知道为什么要用webpack

前端本可以直接HTML、CSS、Javascript就上了,不过如果要处理文件依赖、文件合并压缩、资源管理、使用新技术改善生活的时候,就得利用工具来辅助了。

以往有常见的模块化工具RequireJS,SeaJS等,构建工具Grunt、Gulp等,新的技术Sass、React、ES6、Vue等,要在项目中使用这些东西,不用工具的话就略麻烦了。

其实简单地说要聚焦两点:模块化以及自动构建。

模块化可以使用RequireJS来处理依赖,使用Gulp来进行构建;也可以使用ES6新特性来处理模块化依赖,使用webpack来构建

两种方式都狠不错,但潮流所驱,后者变得愈来愈强大,当然也不是说后者就替代了前者,只是大部分情况下,后者更好

二、什么是Webpack

如其名,Web+Pack 即web的打包,主要用于web项目中打包资源进行自动构建。

Webpack将所有资源视为JS的模块来进行构建,所以对于CSS,Image等非JS类型的文件,Webpack会使用相应的加载器来加载成其可识别的JS模块资源

通过配置一些信息,就能将资源进行打包构建,更好地实现前端的工程化

三、Webpack的基础配置

可以认为Webpack的配置是4+n模式,四个基本的 entry(入口设置)output(输出设置)loader(加载器设置)、plugin(插件设置),然后加上一些特殊功能的配置。

使用Webpack首先需要安装好NodeJS

node -v
npm -v

确保已经可以使用node,使用NPM包管理工具来安装相应依赖包(网络环境差可以使用淘宝镜像CNPM来安装)

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm -v

全局安装好webpack包

npm i -g webpack
webpack -v

1. webpack的配置方式主要有三种

1. 通过cli命令行传入参数 

webpack ./src.js -o ./dest.js --watch --color 

2. 通过在一个配置文件设置相应配置,导出使用

// ./webpack.config.js文件
module.exports = {
   context: ...
entry: { },
output: { }
}; // 命令行调用(不指定文件时默认查找webpack.config.js)
webpack [--config webpack.config.js]

3. 通过使用NodeJS的API配置

这个和第二点有点类似,区别主要是第二种基本都是使用{key: value}的形式配置的,API则主要是一些调用

另外,某些插件的在这两种方式的配置上也有一些区别

最常用的是第二种,其次第三种,第一种不太建议单独使用(因为相对麻烦,功能相对简单)

2. 常见的几个配置属性

1. context  绝对路径

一般当做入口文件(包括但不限于JS、HTML模板等文件)的上下文位置,

默认使用当前目录,不过建议还是填上一个

// 上下文位置
context: path.resolve(__dirname, 'static')

2. entry  模块入口文件设置

可以接受字符串表示一个入口文件,不过一般来说是多页应用多,就设置成每页一个入口文件得了

比如home对应于一个./src/js/home模块,这里的key会被设置成webpack的一个chunk,即最终webpack会又三个chunkname:home | detail | common

也可以对应于多个模块,用数组形式指定,比如这里把jquery设置在common的chunk中

也可以设置成匿名函数,用于动态添加的模块

// 文件入口配置
entry: {
home: './src/js/home',
detail: './src/js/detail',
// 提取jquery入公共文件
common: ['jquery']
},

3. resolve 处理资源的查找引用方式

如上方其实是省略了后JS缀,又比如想在项目中引入util.js 可以省略后缀

import {showMsg} from './components/util';
// 处理相关文件的检索及引用方式
resolve: {
extensions: ['.js', '.jsx', '.json'],
modules: ['node_modules'],
alias: { }
},

4. output 设置文件的输出

最基础的就是这三个了

path指定输出目录,要注意的是这个目录影响范围是比较大,与该chunk相关的资源生成路径是会基于这个路径的

filename指定生成的文件名,可以使用[name] [id]来指定相应chunk的名称,如上的home和detail,用[hash]来指定本次webpack编译的标记来防缓存,不过建议是使用[chunkhash]来依据每个chunk单独来设置,这样不改变的chunk就不会变了

hash放在?号之后的好处是,不会生成新的文件(只是文件内容被更改了),同时hash会附在引用该资源的URL后(如script标签中的引用)

publicPath指定所引用资源的目录,如在html中的引用方式,建议设置一个

// 文件输出配置
output: {
// 输出所在目录
path: path.resolve(__dirname, 'static/dist/js'),
filename: '[name].js?[chunkhash:8]'// 设置文件引用主路径
publicPath: '/public/static/dist/js/'
}

5.devtool指定sourceMap的配置

如果开启了,就可以在浏览器开发者工具查看源文件

// 启用sourceMap
devtool: 'cheap-module-source-map',

比如这里就是对应的一个source Map,建议在开发环境下开启,帮助调试每个模块的代码

这个配置的选项是满多的,而且还可以各种组合,按照自己的选择来吧

6. module指定模块如何被加载

通过设置一些规则,使用相应的loader来加载

主要就是配置module的rules规则组,通过use字段指定loader,如果只有一个loader,可以直接用字符串,loader要设置options的就换成数组的方式吧

或者使用多个loader的时候,也用数组的形式,规则不要用{ }留空,在windows下虽然正常,但在Mac下会报错提示找不到loader

多个loader遵循从右到左的pipe 的方式,如下 eslint-loader是先于babel-loader执行的

通过exclude或include等属性再确定规则的匹配位置

// 模块的处理配置,匹配规则对应文件,使用相应loader配置成可识别的模块
module: {
rules: [{
test: /\.css$/,
use: 'css-loader'
}, {
test: /\.jsx?$/,
// 编译js或jsx文件,使用babel-loader转换es6为es5
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: { }
}, {
loader: 'eslint-loader'
}]
}

7.  plugins设置webpack配置过程中所用到的插件

比如下方为使用webpack自带的提取公共JS模块的插件

// 插件配置
plugins: [
// 提取公共模块文件
new webpack.optimize.CommonsChunkPlugin({
chunks: ['home', 'detail'],
filename: '[name].js',
name: 'common'
}),
new ... ]

这就是webpack最基础的东西了,看起来内容很少,当然还有其他很多,但复杂的地方在于如何真正去使用这些配置

四、Webpack配置在Demo中的应用

下面以一个相对完整的基础Demo着手,介绍一下几个基本功能该如何配置

Demo项目地址   建议拿来练练

1. 搭建个服务器

既然是Demo,至少就得有一个服务器,用node来搭建一个简单的服务器,处理各种资源的请求返回

新建一个服务器文件server.js,以及页面文件目录views,其他资源文件目录public

服务器文件很简单,请求什么就返回什么,外加了一个gzip的功能

let http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url'),
zlib = require('zlib'); http.createServer((req, res) => {
let {pathname} = url.parse(req.url),
acceptEncoding = req.headers['accept-encoding'] || '',
referer = req.headers['Referer'] || '',
raw; console.log('Request: ', req.url); try {
raw = fs.createReadStream(path.resolve(__dirname, pathname.replace(/^\//, ''))); raw.on('error', (err) => {
console.log(err); if (err.code === 'ENOENT') {
res.writeHeader(404, {'content-type': 'text/html;charset="utf-8"'});
res.write('<h1>404错误</h1><p>你要找的页面不存在</p>');
res.end();
}
}); if (acceptEncoding.match(/\bgzip\b/)) {
res.writeHead(200, { 'Content-Encoding': 'gzip' });
raw.pipe(zlib.createGzip()).pipe(res);
} else if (acceptEncoding.match(/\bdeflate\b/)) {
res.writeHead(200, { 'Content-Encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(res);
} else {
res.writeHead(200, {});
raw.pipe(res);
}
} catch (e) {
console.log(e);
}
}).listen(8088); console.log('服务器开启成功', 'localhost:8088/');

2. 设置基础项目目录

页面文件假设采用每一类一个目录,目录下的tpl为源文件,另外一个为生成的目标页面文件

/public目录下,基本配置文件就放在根目录下,JS,CSS,Image等资源文件就放在/public/static目录下

我们要利用package.json文件来管理编译构建的包依赖,以及设置快捷的脚本启动方式,所以,先在/public目录下执行 npm init 吧

public/static/dist目录用来放置编译后的文件目录,最终页面引用的将是这里的资源

public/static/imgs目录用来放置图片源文件,有些图片会生成到dist中

public/static/libs目录主要用来放置第三方文件,也包括那些很少改动的文件

public/static/src 用来放置js和css的源文件,相应根目录下暴露一个文件出来,公共文件放到相应子目录下(如js/components和scss/util)

 

最后文件结构看起来是这样的,那就可以开干了

3. 开发和生产环境的Webpack配置文件区分

首先在项目目录下安装webpack吧

npm i webpack --save-dev

用Webpack来构建,在开发环境和生产环境的配置还是有一些区别的,构建是耗时的,比如在开发环境下就不需要压缩文件、计算文件hash、提取css文件、清理文件目录这些辅助功能了,而可以引入热更新替换来加快开发时的模块更新效率。

所以建议区分一下两个环境,同时将两者的共同部分提取出来便于维护

NODE_ENV是nodejs在执行时的环境变量,webpack在运行构建期间也可以访问这个变量,所以我们可以在dev和prod下配置相应的环境变量

这个配置写在package.json里的scripts字段就好了,比如

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:dev": "export NODE_ENV=development && webpack-dev-server --config webpack.config.dev.js",
"build:prod": "export NODE_ENV=production && webpack --config webpack.config.prod.js --watch "
},

这样一来,我们就可以直接用 npm run build:prod来执行生产环境的配置命令(设置了production的环境变量,使用prod.js)

直接用npm run build:dev来执行开发环境的配置命令(设置了development的环境变量,使用dev.js,这里还使用了devServer,后面说)

注意这里是Unix系统配置环境变量的写法,在windows下,记得改成 SET NODE_ENV=development&& webpack-dev-server.......(&&前不要空格)

然后就可以在common.js配置文件中获取环境变量

// 是否生产环境
isProduction = process.env.NODE_ENV === 'production',

然后可以在plugins中定义一个变量提供个编译中的模块文件使用

// 插件配置
plugins: [
// 定义变量,此处定义NODE_ENV环境变量,提供给生成的模块内部使用
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
}),

这样一来,我们可以在home.js中判断是否为开发环境来引入一些文件

// 开发环境时,引入页面文件,方便改变页面文件后及时模块热更新
if (process.env.NODE_ENV === 'development') {
require('../../../../views/home/home.html');
}

然后我们使用webpack-merge工具来合并公共配置文件和开发|生产配置文件

npm i webpack-merge --save-dev

merge = require('webpack-merge')

commonConfig = require('./webpack.config.common.js')

/**
* 生产环境Webpack打包配置,整合公共部分
* @type {[type]}
*/
module.exports = merge(commonConfig, {
// 生产环境不开启sourceMap
devtool: false, // 文件输出配置
output: {
// 设置文件引用主路径
publicPath: '/public/static/dist/js/'
}, // 模块的处理配置

4. 设置公共模块

公共模块其实可以分为JS和CSS两部分(如果有提取CSS文件的话)

在公共文件的plugin中加入

// 提取公共模块文件
new webpack.optimize.CommonsChunkPlugin({
chunks: ['home', 'detail'],
// 开发环境下需要使用热更新替换,而此时common用chunkhash会出错,可以直接不用hash
filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
name: 'common'
}),

设置公共文件的提取源模块chunks,以及最终的公共文件模块名

公共模块的文件的提取规则是chunks中的模块公共部分,如果没有公共的就不会提取,所以最好是在entry中就指定common模块初始包含的第三方模块,如jquery,react等

 // 文件入口配置
entry: {
home: './src/js/home',
detail: './src/js/detail',
// 提取jquery入公共文件
common: ['jquery']
},

5. 编译ES6成ES5

要讲ES6转换为ES5,当然首用babel了,先安装loader及相关的包

npm i babel-core babel-loader babel-preset-env babel-polyfill babel-plugin-transform-runtime --save-dev

-env包主要用来配置语法支持度

-polyfill用来支持一些ES6拓展的但babel转换不了的方法(Array.from Generator等)

-runtime用来防止重复的ES6编译文件所需生成(可以减小文件大小)

然后在/public根目录下新建 .babelrc文件,写入配置

{
"presets": [
"env"
],
"plugins": ["transform-runtime"]
}

然后在common.js的配置文件中新增一条loader配置就行了,注意使用exclude排除掉不需要转换的目录,否则可能会出错哦

{
test: /\.jsx?$/,
// 编译js或jsx文件,使用babel-loader转换es6为es5
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: { }
}]
}

6. 编译Sass成CSS,嵌入到页面<style>标签中,或将其提取出(多个)CSS文件来用<link>引入

sass的编译node-sass需要python2.7的环境,先确定已经安装并设置了环境变量

npm i sass-loader node-sass style-loader css-loader --save-dev

类似的,设置一下loader规则

不过这里要设置成使用提取CSS文件的插件设置了,因为它的disable属性可以快速切换是否提取CSS(这里设置成生产环境才提取)

好好看这个栗子,其实分三步:设置(new)两个实例,loader匹配css和sass两种文件规则,在插件中引入这两个实例

提取多个CSS文件其实是比较麻烦的,但也不是不可以,方法就是设置多个实例和对应的几个loader规则

这里把引入的sass当做是自己写的文件,提取成一个文件[name].css,把引入的css当做是第三方的文件,提取成一个[name]_vendor.css,既做到了合并,也做到了拆分,目前还没想到更好的方案

上面提到过,output的path设置成了/public/static/dist/js ,所以这里的filename 生成是基于上面的路径,可以用../来更换生成的css目录

[contenthash]是css文件内容的hash,在引用它的地方有体现

fallback表示不可提取时的代替方案,即上述所说的使用style-loader嵌入到<style>标签中

npm i extract-text-webpack-plugin --save-dev

ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')

/ 对import 引入css(如第三方css)的提取
cssExtractor = new ExtractTextWebpackPlugin({
// 开发环境下不需要提取,禁用
disable: !isProduction,
filename: '../css/[name]_vendor.css?[contenthash:8]',
allChunks: true
}) // 对import 引入sass(如自己写的sass)的提取
sassExtractor = new ExtractTextWebpackPlugin({
// 开发环境下不需要提取,禁用
disable: !isProduction,
filename: '../css/[name].css?[contenthash:8]',
allChunks: true
}); // 插件配置
plugins: [
// 从模块中提取CSS文件的配置
cssExtractor,
sassExtractor
] module: {
rules: [{
test: /\.css$/,
// 提取CSS文件
use: cssExtractor.extract({
// 如果配置成不提取,则此类文件使用style-loader插入到<head>标签中
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
// url: false,
minimize: true
}
},
// 'postcss-loader'
]
})
}, {
test: /\.scss$/,
// 编译Sass文件 提取CSS文件
use: sassExtractor.extract({
// 如果配置成不提取,则此类文件使用style-loader插入到<head>标签中
fallback: 'style-loader',
use: [
'css-loader',
// 'postcss-loader',
{
loader: 'sass-loader',
options: {
sourceMap: true,
outputStyle: 'compressed'
}
}
]
})
}

这样一来,如果在不同文件中引入不同的文件,生成的css可能长这样

// ./home.js
import '../../libs/bootstrap-datepicker/datepicker3.css'; import '../../libs/chosen/chosen.1.0.0.css'; import '../../libs/layer/skin/layer.css'; import '../../libs/font-awesome/css/font-awesome.min.css'; import '../scss/detail.scss'; // ./detail.js
import '../../libs/bootstrap-datepicker/datepicker3.css'; import '../../libs/chosen/chosen.1.0.0.css'; import '../../libs/layer/skin/layer.css'; import '../scss/detail.scss';

// ./home.html
<link href="/public/static/dist/js/../css/common_vendor.css?66cb1f48" rel="stylesheet">
<link href="/public/static/dist/js/../css/common.css?618d2a04" rel="stylesheet">
<link href="/public/static/dist/js/../css/home_vendor.css?12a314c8" rel="stylesheet">
<link href="/public/static/dist/js/../css/home.css?c196fc33" rel="stylesheet"> // ./detail.html
<link href="/public/static/dist/js/../css/common_vendor.css?66cb1f48" rel="stylesheet">
<link href="/public/static/dist/js/../css/common.css?618d2a04" rel="stylesheet">

可以看到,公共文件也被提取出来了,利用HtmlWebpackPlugin就能将其置入了

另外,可以看到这里的绝对路径,其实就是因为在output中设置了publicPath为/public/static/dist/js/

当然了,也不是说一定得在js中引入这些css资源文件,你可以直接在页面中手动<link>引入第三方CSS

我这里主要是基于模块化文件依赖,以及多CSS文件的合并压缩的考虑才用这种引入方式的

7. jQuery插件的引入方式

目前来说,jQuery及其插件在项目中还是很常用到的,那么就要考虑如何在Webpack中使用它

第一种方法,就是直接页面中<script>标签引入了,但这种方式不受模块化的管理,好像有些不妥

第二种方法,就是直接在模块中引入所需要的jQuery插件,而jQuery本身由Webpack插件提供,通过ProvidePlugin提供模块可使用的变量$|jQuery|window.jQuery

不过这种方法好像也有不妥,把所有第三方JS都引入了,可能会降低编译效率,生成的文件也可能比较臃肿

npm i jquery --save

//
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}), ] // ./home.js import '../../libs/bootstrap-datepicker/bootstrap-datepicker.js';
console.log('.header__img length', jQuery('.header__img').length);

第三种办法,可以在模块内部直接引入jQuery插件,也可以直接在页面通过<script>标签引入jQuery插件,而jQuery本身由Webpack的loader导出为全局可用

上述ProvidePlugin定义的变量只能在模块内部使用,我们可以使用expose-loader将jQuery设置为全局可见

npm i expose-loader --save

// 添加一条规则
{
test: require.resolve('jquery'),
// 将jQuery插件变量导出至全局,提供外部引用jQuery插件使用
use: [{
loader: 'expose-loader',
options: '$'
}, {
loader: 'expose-loader',
options: 'jQuery'
}]
}

要注意在Webpack3中不能使用webpack.NamedModulesPlugin()来获取模块名字,它会导致expose 出错失效(bug)

不过现在问题又来了,这个应该是属于HtmlWebpackPlugin的不够机智的问题,先说说它怎么用吧

8. HtmlWebpackPlugin将页面模板编译成最终的页面文件,包含JS及CSS资源的引用

第一个重要的功能就是生成对资源的引入了,第二个就是帮助我们填入资源的chunkhash值防止浏览器缓存

这个在生产环境使用就行了,开发环境是不需要的

npm i html-webpack-plugin --save-dev

HtmlWebpackPlugin = require('html-webpack-plugin')

plugins: [

 // 设置编译文件页面文件资源模块的引入
new HtmlWebpackPlugin({
// 模版源文件
template: '../../views/home/home_tpl.html',
// 编译后的目标文件
filename: '../../../../views/home/home.html',
// 要处理的模块文件
chunks: ['common', 'home'],
// 插入到<body>标签底部
inject: true
}),
new HtmlWebpackPlugin({
template: '../../views/detail/detail_tpl.html',
filename: '../../../../views/detail/detail.html',
chunks: ['common', 'detail'],
inject: true
}), ]

使用方式是配置成插件的形式,想对多少个模板进行操作就设置多少个实例

注意template是基于context配置中的上下文的,filename是基于output中的path路径的

// ./home_tpl.html

    <script src="/public/static/libs/magicsearch/jquery.magicsearch2.js"></script>
</body> // ./home.html <script src=/public/static/libs/magicsearch/jquery.magicsearch2.js></script>
<script type="text/javascript" src="/public/static/dist/js/common.js?cc867232"></script>
<script type="text/javascript" src="/public/static/dist/js/home.js?5d4a7836"></script>
</body>

它会编译成这样,然而,然而,要注意到这里是有问题的

这里有个jQuery插件,而Webpack使用expose是将jQuery导出到了全局中,我们通过entry设置把jQuery提取到了公共文件common中

所以正确的做法是common.js文件先于jQuery插件加载

而这个插件只能做到在<head> 或<body>标签尾部插入,我们只好手动挪动一下<script>的位置

不过,我们还可以基于这个插件,再写一个插件来实现自动提升公共文件 <script>标签到最开始

HtmlWebpackPlugin运行时有一些事件

    html-webpack-plugin-before-html-generation
html-webpack-plugin-before-html-processing
html-webpack-plugin-alter-asset-tags
html-webpack-plugin-after-html-processing
html-webpack-plugin-after-emit
html-webpack-plugin-alter-chunks

在编译完成时,正则匹配到<script>标签,找到所设置的公共模块(可能设置了多个公共模块),按实际顺序提升这些公共模块即可

完整代码如下:

 1 // ./webpack.myPlugin.js
2
3
4 let extend = require('util')._extend;
5
6
7 // HtmlWebpackPlugin 运行后调整公共script文件在html中的位置,主要用于jQuery插件的引入
8 function HtmlOrderCommonScriptPlugin(options) {
9 this.options = extend({
10 commonName: 'common'
11 }, options);
12 }
13
14 HtmlOrderCommonScriptPlugin.prototype.apply = function(compiler) {
15 compiler.plugin('compilation', compilation => {
16 compilation.plugin('html-webpack-plugin-after-html-processing', (htmlPluginData, callback) => {
17 // console.log(htmlPluginData.assets);
18
19 // 组装数组,反转保证顺序
20 this.options.commonName = [].concat(this.options.commonName).reverse();
21
22 let str = htmlPluginData.html,
23 scripts = [],
24 commonScript,
25 commonIndex,
26 commonJS;
27
28 //获取编译后html的脚本标签,同时在原html中清除
29 str = str.replace(/(<script[^>]*>(\s|\S)*?<\/script>)/gi, ($, $1) => {
30 scripts.push($1);
31 return '';
32 });
33
34 this.options.commonName.forEach(common => {
35 if (htmlPluginData.assets.chunks[common]) {
36 // 找到公共JS标签位置
37 commonIndex = scripts.findIndex(item => {
38 return item.includes(htmlPluginData.assets.chunks[common].entry);
39 });
40
41 // 提升该公共JS标签至顶部
42 if (commonIndex !== -1) {
43 commonScript = scripts[commonIndex];
44 scripts.splice(commonIndex, 1);
45 scripts.unshift(commonScript);
46 }
47 }
48 });
49
50 // 重新插入html中
51 htmlPluginData.html = str.replace('</body>', scripts.join('\r\n') + '\r\n</body>');
52
53 callback(null, htmlPluginData);
54 });
55 });
56 };
57
58
59 module.exports = {
60 HtmlOrderCommonScriptPlugin,
61 };

然后,就可以在配置中通过插件引入了

{HtmlOrderCommonScriptPlugin} = require('./webpack.myPlugin.js');

// HtmlWebpackPlugin 运行后调整公共script文件在html中的位置,主要用于jQuery插件的引入
new HtmlOrderCommonScriptPlugin({
// commonName: 'vendor'
})

亲测还是蛮好用的,可以应对简单的需求了

9. 使用url-loader和file-loader和html-loader来处理图片、字体等文件的资源引入路径问题

这个配置开发环境和生产环境是不同的,先看看生产环境的,主要的特点是有目录结构的设置,设置了一些生成的路径以及名字信息

开发环境因为是使用了devServer,不需要控制目录结构

npm i url-loader file-loader@0.10.0 html-loader --save-dev

这里要注意的是file-loader就不要用0.10版本以上的了,会出现奇怪的bug,主要是下面设置的outputPath和publicPath和[path]会不按套路出牌

导致生成的页面引用资源变成奇怪的相对路径

rules: [{
test: /\.(png|gif|jpg)$/,
use: {
loader: 'url-loader',
// 处理图片,当大小在范围之内时,图片转换成Base64编码,否则将使用file-loader引入
options: {
limit: 8192,
// 设置生成图片的路径名字信息 [path]相对context,outputPath输出的路径,publicPath相应引用的路径
name: '[path][name].[ext]?[hash:8]',
outputPath: '../',
publicPath: '/public/static/dist/',
}
}
}, {
test: /\.(eot|svg|ttf|otf|woff|woff2)\w*/,
use: [{
loader: 'file-loader',
options: {
// 设置生成字体文件的路径名字信息 [path]相对context,outputPath输出的路径,publicPath相应引用的主路径
name: '[path][name].[ext]?[hash:8]',
outputPath: '../',
publicPath: '/public/static/dist/',
// 使用文件的相对路径,这里先不用这种方式
// useRelativePath: isProduction
}
}],
}, {
test: /\.html$/,
// 处理html源文件,包括html中图片路径加载、监听html文件改变重新编译等
use: [{
loader: 'html-loader',
options: {
minimize: true,
removeComments: false,
collapseWhitespace: false
}
}]
}]

比较生涩难懂,看个栗子吧

scrat.png是大于8192的,最终页面引入会被替换成绝对路径,并且带有hash防止缓存,而输出的图片所在位置也是用着相应的目录,便于管理

// ./home_tpl.html

        <img class="header__img" src="../../public/static/imgs/kl/scrat.png" width="200" height="200">

// ./home.html

        <img class="header__img" src=/public/static/dist/imgs/kl/scrat.png?8ad54ef5 width=200 height=200>

如果换个小图,就会替换成base64编码了,在css中的引入也一样

<img class=header__img src=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAA6CAYAAABrnUYFAAAaVElEQVR4Xu1df3wdVZX/npnkNUlbWn6sFhAELSgibSYtCIhagXXB0vdSsOwu4CKiXbAglk8zAVwloELzwoqIUsFFRXQVaiEvRcAfLD9EfqxtXtqKS239AQoFRUlb+tK+5N2znzNvJp1M34+ZeZOElLn/QPPuPffcc2e+c+75dQlxiyUQSyCWwChJgEaJbkw2lkAsgVg

再来看看开发环境的

rules: [{
test: /\.(png|gif|jpg)$/,
// 处理图片,当大小在范围之内时,图片转换成Base64编码,否则将使用file-loader引入
use: [{
loader: 'url-loader',
options: {
limit: 8192
}
}]
}, {
test: /\.(eot|svg|ttf|otf|woff|woff2)\w*/,
// 引入文件
use: 'file-loader'
}]

10. 模块热更新替换的正确姿势

在开发环境下,如果做到模块的热更新替换,效果肯定是棒棒的。生成环境就先不用了

在最初的时候,只是做到了热更新,并没有做到热替换,其实都是坑在作祟

热更新,需要一个配置服务器,Webpack集成了devServer的nodejs服务器,配置一下它

// 开发环境设置本地服务器,实现热更新
devServer: {
contentBase: path.resolve(__dirname, 'static'),
// 提供给外部访问
host: '0.0.0.0',
port: 8188,
// 设置页面引入
inline: true
},

正常的话,启动服务应该就可以了吧

webpack-dev-server --config webpack.config.dev.js

要记住,devServer编译的模块是输出在服务器上的(默认根目录),不会影响到本地文件,所以要在页面上手动设置一下引用的资源

<script src="http://localhost:8188/common.js"></script>
<script src="http://localhost:8188/home.js"></script>

浏览器访问,改动一下home.js文件,这时应该可以看到页面自动刷新,这就是热更新了

webpack配置指南的更多相关文章

  1. 在找一份相对完整的Webpack项目配置指南么?这里有

    Webpack已经出来很久了,相关的文章也有很多,然而比较完整的例子却不是很多,让很多新手不知如何下脚,下脚了又遍地坑 说实话,官方文档是蛮乱的,而且有些还是错的错的..很多配置问题只有爬过坑才知道 ...

  2. Webpack 入门指南 - 2.模块

    这一次我们谈谈模块问题. 通常我们希望这个项目可以分为多个独立的模块,比如,上一次提高的 hello 函数,如果我们定义为一个模块,其它模块引用之后,直接调用就好了.在前端怎么使用模块呢?这可说来话长 ...

  3. Webpack 入门指南 - 1.安装

    Webpack 是目前流行的打包工具,如何安装它呢? 1. 安装 Node Js 首先,Webpack 是基于 NodeJs 的工具,你必须首先安装 NodeJs. NodeJs 仅仅只需要在你的系统 ...

  4. webpack配置详解

    webpack配置详解 先点个赞吧,再挨个点下面的连接,觉得不值这个赞的回来骂我啊. Webpack傻瓜式指南(一) Webpack傻瓜指南(二)开发和部署技巧 Webpack傻瓜式指南 原生的官网详 ...

  5. Vux配置指南

    流程 Vux是Vue.js的一个ui库,官网在这里,官方文档的配置指南侧重于技术的罗列,我这里简化一下Vux的配置流程. 1. 安装vux npm install vux --save 2. 安装le ...

  6. webpack4 中的最新 React全家桶实战使用配置指南!

    最新React全家桶实战使用配置指南 这篇文档 是吕小明老师结合以往的项目经验 加上自己本身对react webpack redux理解写下的总结文档,总共耗时一周总结下来的,希望能对读者能够有收获, ...

  7. Visual Studio Code 配置指南

    Visual Studio Code (简称 VS Code)是由微软研发的一款免费.开源的跨平台文本(代码)编辑器.在我看来它是「一款完美的编辑器」. 本文是有关 VS Code 的特性介绍与配置指 ...

  8. [webpack] 配置react+es6开发环境

    写在前面 每次开新项目都要重新安装需要的包,简单记录一下. 以下仅包含最简单的功能: 编译react 编译es6 打包src中入口文件index.js至dist webpack配置react+es6开 ...

  9. Webpack 入门指南 - 3. Hello, Angular2!

    Webpack 入门指南 - 1.安装 Webpack 入门指南 - 2.模块 这一次,我们使用 Webpack 来打包 Angular 2 的应用. 与官方的 Hello, Angular 2 项目 ...

随机推荐

  1. [UE4]C++静态加载问题:ConstructorHelpers::FClassFinder()和FObjectFinder()

    http://aigo.iteye.com/blog/2281373 原文作者:@玄冬Wong 相关内容: C++实现动态加载的问题:LoadClass<T>()和LoadObject&l ...

  2. 洛谷U3348 A2-回文数

    U3348 A2-回文数 题目背景 方方方很喜欢回文数,于是就有了一道关于回文数的题目. 题目描述 求从小到大第n(1<=n<=10^18)个回文数. 注释:出题人认为回文数不包括0. 输 ...

  3. 洛谷P4099 [HEOI2013]SAO(树形dp)

    传送门 HEOI的题好珂怕啊(各种意义上) 然后考虑树形dp,以大于为例 设$f[i][j]$表示$i$这个节点在子树中排名第$j$位时的总方案数(因为实际只与相对大小有关,与实际数值无关) 我们考虑 ...

  4. mySQL多表查询与事务

    一.范式 1. 什么是范式 1.1 什么是范式 范式:设置一个科学的.规范的数据库,需要满足的一些规则 1.2 有哪些范式 共有:6大范式 第1范式:1NF 满足最基本的要求 第2范式:2NF 在1N ...

  5. C#代码生成器附百度云盘源码地址

    今晚闲着没事,写了个代码生成器,在这里只做个抛砖引玉,后面可以继续扩展功能,下方附百度云盘源码地址. 使用数据库:sqlserver 编译器:vs2015 废话不多说,上界面: 程序主界面: 数据库: ...

  6. 删除vi编辑产生的.swp文件(linux编辑文件没有退出时直接关闭程序产生的临时文件)

    关于swp文件 使用vi,经常可以看到swp这个文件,那这个文件是怎么产生的呢,当你打开一个文件,vi就会生成这么一个.(filename)swp文件以备不测(不测下面讨论),如果你正常退出,那么这个 ...

  7. hibernate Restrictions用法 HibernateTemplate Hibernate结合spring

    常用方法 http://www.jb51.net/article/41541.htm ........................................... 博客分类: Hiberna ...

  8. Prime Count 求大区间素数个数

    http://acm.gdufe.edu.cn/Problem/read/id/1333 https://www.zhihu.com/question/29580448/answer/44874605

  9. PHP正则表达式 - 附录(常用正则表达式)

    常用正则表达式附录I 平时做网站经常要用正则表达式,下面是一些讲解和例子,仅供大家参考和修改使用: "^\d+$" //非负整数(正整数 + 0) "^[0-9]*[1- ...

  10. MVC的viewPage 通用属性运用。

    试想下在MVC的前端页面JS或者html中需要使用多语言,而后端的多语言是维护在资源文件中的,前端如果使用的话需要使用AJAX频繁的获取,一个页面中可能会存在大量的需要语言转换的地方,频繁使用AJAX ...