一、webpack3.x.x基本介绍
1-1 webpack基本介绍
webpack的概念和作用
模块打包器
代码分割
代码文件打包
loader加载
commonjs规范
模块热更新
适用于大型项目1-2 webpack安装和命令
安装
npm install webpack --save-dev
例子:
新建hello.js
执行命令 webpack hello.js hello.bundle.js
,会生成hello.bundle.js
文件,观察文件中的内容,可以看到webpack
在文件中生成了许多其他代码
新建world.js
并在hello.js
文件中require('./world.js')
,再执行命令webpack hello.js hello.bundle.js
,观察hello.bundle.js
文件中的内容
新建style.css
,在样式表中随便新建几行样式html,body{background: #f00;}
,在hello.js
文件中require('style-loader!css-loader!./style.css')
,执行命令webpack hello.js hello.bundle.js
(注意:用webpack
加载css
需要用到css-loader
,行内样式需要用到style-loader
。安装loader npm install css-loader style-loader --save-dev)
css-loader
用来将style.css
打包到hello.bundle.js
文件中,使webpack
能处理.css文件,如果将css-loader
处理后的文件,引入到页面中后(在这个例子中引入页面的是hello.bundle.js
),style-loader
则会将style.css
中的样式以<style></style>
的形式插入到head
中
如果在页面中没有加载这两个loader
也可以在命令行中直接加载loader
webpack hello.js hello.bundle.js --module-bind "css=style-loader!css-loader"
输入webpack
可以查看webpack
中有哪些配置项
webpack hello.js hello.bundle.js --watch
用来监听文件,如果文件有变化则自动编译打包
webpack hello.js hello.bundle.js --progress
用来查看打包的进度
webpack hello.js hello.bundle.js --display-modules
用来查看打包的模块
webpack hello.js hello.bundle.js --display-resons
用来查看打包的原因
二、webpack基本配置
- 2-1建立项目的webpack配置
例子:
在webpack-demo
目录下新建以下文件
webpack-demo
-dist
--js
-src
--script
--style
-index.html
-package.json
-webpack.config.js
webpack.config.js
module.exports = {
entry: './src/script/main.js',//入口文件
output: {
path: './dist/js',//输出路径
filename: 'bundle.js'//输出文件名
}
}
运行 webpack
会在dist
下的js目录下生成bundle.js
可在package.json
的script
字段中添加配置webpack
"scripts": {
"webpack":"webpack --config webpack.config.js --progress --display-modules --colors --display-reasons"
},
- 2-2
webpack
配置的entry
和output
entry
是入口文件
①entry
可以是一个字符串
entry:'a.js'
{
context: __dirname + "/app",
entry: "./entry",
output: {
path: __dirname + "/dist",
filename: "bundle.js"
}
}
②entry
可以是一个数组
数组中的文件一般是有相互依赖关系的,或者是没有互相依赖关系但是又处于某些原因需要将它们打包在一起
entry:['a.js','main.js']
③entry
可以是一个对象({key:value}
)常用于多页面(多入口)的情况下配置
key
key可以是简单的字符串,比如:’app’, ‘main’,’entry-1’等。并且对应着output.filename配置中的[name]变量key还可以是路径字符串。此时webpack会自动生成路径目录,并将路径的最后作为[name]。这个特性在多页面配置下也是很有用的
entry: {
'path/of/entry': './deep-app.js',
'app': './app.js'
},
output: {
path: './output',
filename: '[name].js'
}
value
value如果是字符串,而且必须是合理的node require函数参数字符串。比如文件路径:’./app.js’(require(‘./app.js’));比如安装的npm模块:’lodash’(require(‘lodash’))
value如果是数组,则数组中元素需要是上面描述的合理字符串值。数组中的文件一般是没有相互依赖关系的,但是又处于某些原因需要将它们打包在一起。比如:
output: {
path: _dirname+'/built',//输出路径
filename: '[name].js'//输出文件名
filename: '[name]-[hash].js'//输出文件名
filename: '[name]-[chunkhash].js'//输出文件名
}
entry:{
page1:'./page1'
page2:['entry1.js','entry2.js']
}
在entry
是多入口的情况下,output也应该是多个输出 filename
有三个可选参数 [name]
、[hash]
、[chunkhash]
output: {
path: _dirname+'/built',//输出路径
filename: '[name].js'//输出文件名
}
注意,在不同的版本下可能会报路径不是绝对路径的错误,解决办法为
var path = require('path');
module.exports = {
entry: [url('src/script/main.js'), url('src/script/a.js')],
output: {
path: url('./dist/js/'),
filename: 'bundle.js'
}
}
function url(url) {
return path.resolve(__dirname, url)
}
三、生成项目中的html文件
- 3-1自动化生成项目中的html文件(上)
安装
html-webpack-plugin
(这里有个坑就是,如果在全局安装webpack,在运行npm run webpack之前需要运行 npm link webpack
)
自动生成html的意义在于,html
中引用的js
文件好多都是webpack
打包之后生成的,生成的文件名和目录都有可能改变,这个时候不能手动来修改每一个变动的路径,太耗时,这个时候就要通过html-webpack-plugins
这个插件来自动在html中生成对js的引用
在webpack.config.js
中加入plugins
配置项
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
main: './src/script/main.js',
a: './src/script/a.js'
},
output: {
path: './dist',
filename: 'js/[name]-[hash].js'
},
plugins: [
new htmlWebpackPlugin({
template:'index.html',
inject:'head'
})
]
}
在htmlWebpackPlugin
插件中配置项template
用来定义引用的模板文件,inject
用来指定脚本在dom
文档中的插入位置
- 3-2自动化生成项目中的html文件(中)
template
应用场景
在参数中传参,在模板中引用
plugins: [
new htmlWebpackPlugin({
template:'index.html',
inject:'head',
filename:'index-[hash].html',
title:'webpack is good'//传参
date:new Date()//传参
})
]
在html的title中,以如下的方式引用
<title><%= htmlWebpackPlugin.options.title %></title>
<%= htmlWebpackPlugin.options.title %>这种方式的引用在模板中的任何一个位置都可以,例如
plugins: [
new htmlWebpackPlugin({
template:'index.html',
inject:'head',
title:'webpack is good'
date:new Date()
})
]
<body>
<%= htmlWebpackPlugin.options.date %>
</body>
这样在编译后的index.html
文件中会包含日期信息Tue Apr 04 2017 23:25:58 GMT+0800 (中国标准时间)
在html文件中body标签中,输入如下代码,运行 npm run webpack
<% for(var key in htmlWebpackPlugin){ %>
<%= key %>
<% } %>
生成的文件中会有files
options
,这两个值说明在htmlWebpackPlugin
插件中存在这两个对象
继续遍历htmlWebpackPlugin.files
htmlWebpackPlugin.options
这两个对象
<% for(var key in htmlWebpackPlugin.files){ %>
<%= key %>:<%= JSON.stringify(htmlWebpackPlugin.files[key])%>
<% } %>
<% for(var key in htmlWebpackPlugin.options){ %>
<%= key %>:<%= JSON.stringify(htmlWebpackPlugin.options[key])%>
<% } %>
得到
//files
publicPath:""
chunks:{"main":{"size":27,"entry":"js/main-c6074c5f07c44041cf45.js","hash":"ddd0a31267a8793ca2d8","css":[]},"a":{"size":14,"entry":"js/a-c6074c5f07c44041cf45.js","hash":"ef609b383ce569e49d38","css":[]}}
js:["js/main-c6074c5f07c44041cf45.js","js/a-c6074c5f07c44041cf45.js"]
css:[]
manifest:
//options
template:"F:\\localhost\\wamp\\www\\webpack-demo\\node_modules\\._html-webpack-plugin@2.28.0@html-webpack-plugin\\lib\\loader.js!F:\\localhost\\wamp\\www\\webpack-demo\\index.html"
filename:"index.html"
hash:false
inject:"head"
compile:true
favicon:false
minify:false
cache:true
showErrors:true
chunks:"all"
excludeChunks:[]
title:"webpack is good"
xhtml:false
date:"2017-04-04T15:45:55.712Z"
//压缩
minify:{
removeComments:true,//删除注释
collapseWhitespace:true//删除空格
}
可以用<%= %>
这种灵活的方式实现更多的个性化定制
- 3-3自动化生成项目中的html文件(下)
多文件形式/多页面应用
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
main: './src/script/main.js',
a: './src/script/a.js',
b: './src/script/b.js',
c: './src/script/c.js'
},
output: {
path: './dist',
filename: 'js/[name]-[hash].js'
},
plugins: [
new htmlWebpackPlugin({
filename:'main.html',
template: 'index.html',
title: 'webpack is main',
date: new Date()
}),
new htmlWebpackPlugin({
filename:'a.html',
template: 'index.html',
title: 'webpack is a',
date: new Date()
}),
new htmlWebpackPlugin({
filename:'b.html',
template: 'index.html',
title: 'webpack is b',
date: new Date()
}),
new htmlWebpackPlugin({
filename:'c.html',
template: 'index.html',
title: 'webpack is c',
date: new Date()
})
]
}
chunks用来指定匹配的生成文件中应该生成数组中的文件(excludeChunks和chunks相反)
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
main: './src/script/main.js',
a: './src/script/a.js',
b: './src/script/b.js',
c: './src/script/c.js'
},
output: {
path: './dist',
filename: 'js/[name]-[hash].js'
},
plugins: [
new htmlWebpackPlugin({
filename:'main.html',
template: 'index.html',
title: 'webpack is main',
date: new Date(),
chunks:['a','b','c']
}),
new htmlWebpackPlugin({
filename:'a.html',
template: 'index.html',
title: 'webpack is a',
date: new Date(),
chunks:['b','c']
}),
new htmlWebpackPlugin({
filename:'b.html',
template: 'index.html',
title: 'webpack is b',
date: new Date(),
chunks:['a','c']
}),
new htmlWebpackPlugin({
filename:'c.html',
template: 'index.html',
title: 'webpack is c',
date: new Date(),
chunks:['a','b']
})
]
}
main.html
中会生成 a.js b.js c.js
a.html
中会生成b.js c.js
b.html
中会生成a.js c.js
c.html
中会生成a.js b.js
将生成的脚本变成内联脚本
compilation.assets[htmlWebpackPlugin.files.chunks.main.entry].source()
如果在申明publicPath
的情况下代码应该是这样
compilation.assets[htmlWebpackPlugin.files.chunks.main.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source()
htmlWebpackPlugin.files.publicPath.length
得到publicPath
的长度例如 https://www.baidu.com
htmlWebpackPlugin.files.chunks.main.entry.substr()
得到文件真实路径
最后用compilation.assets[].source()
得到内容
在main.js
已经以内联的方式插入到文档,那么多后续的出来main.js
则不需要任何操作,可以在index.html
里面写入这样的模板语法,来判断是否是main.js
以外的文件,如果不是则插入
四、处理项目中的资源文件
- 4-1什么是
loader
以及loader
的特性
加载器是应用于模块源代码的转换。它们允许您在导入或“加载”它们时预处理文件。因此,加载器就像是其他构建工具中的“任务”,并提供了一种强大的方法来处理前端构建步骤。加载器可以将文件从不同的语言(如打字稿)转换为JavaScript,或者将内联图像转换为数据url。加载器甚至允许您直接从JavaScript模块导入CSS文件
有3种使用loader的方式
1. 在webpack.config.js文件中指定
module: {
rules: [{
test: /\.css$/,
use: [{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true
}
}]
},
{
test: /\.jsx?$/,
include: [path.resolve(__dirname, "app")],
exclude: [path.resolve(__dirname, "app/demo-files")],
issuer: {
test,
include,
exclude
},
enforce: "pre",
enforce: "post",
loader: "babel-loader",
options: {
presets: ["es2015"]
},
},
{
test: /\.html$/,
use: [
// apply multiple loaders and options
"htmllint-loader", {
loader: "html-loader",
options: {}
}]
},
]
}
- 在每个import语句中显式地指定它们
require('style-loader!css-loader!./style.css')
import Styles from 'style-loader!css-loader?modules!./styles.css'
在命令行中使用
webpack --module-bind 'css=style-loader!css-loader'
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
- 4-2使用babel-loader转换ES6代码
npm install –save-dev babel-loader babel-core babel-preset-env
cnpm install --save-dev babel-loader babel-core babel-preset-es2015
cnpm install babel-preset-env --save-dev
webpack-demo
-dist
-node_modules
-src
--components
---layer
----layer.html
----layer.js
----layer.css
--app.js
-index.html
-package.json
-webpack.config.js
const htmlWebpackPlugin=require('html-webpack-plugin');
const path = require('path');
module.exports={
entry:'./src/app.js',
output:{
path:__dirname+'/dist',
filename:'[name].bundle.js'
},
module:{
rules:[
{
test:/\.js$/,
include:path.resolve(__dirname, "src"),
exclude: path.resolve(__dirname, "node_modules"),
use:{
loader: "babel-loader",
options:{
"presets": [
"es2015"
]
}
}
}
]
},
plugins:[
new htmlWebpackPlugin({
title:'app',
inject:'head',
template:'index.html',
filename:'index.html',
minify:{
collapseWhitespace:true
}
})
]
}
presets:['latest']
也可以放在package.json
文件中
"babel":{
"presets": [
"es2015"
]
}
或者放在.babelrc
中
{
"presets": [
"es2015"
]
}
注意,webpack
目前只在您的node_modules
文件夹中搜索加载器。如果这些加载器是在您的node_modules
文件夹之外定义的,那么您需要使用resolveLoader
属性来获得webpack
来包含您的loader
。例如,假设您的自定义加载器包含在一个名为loaders
的文件夹中。您需要在配置文件中添加以下内容:
resolveLoader: {
modules: ['node_modules', path.resolve(__dirname, 'loaders')]
}
path.resolve(__dirname+'')
,用来将相对路径改为绝对路径
cnpm install --save-dev babel-preset-latest
cnpm install --save-dev babel
注意:在webpack2.0
中 有些不一样
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: path.resolve(__dirname + 'node_modules'),
include: path.resolve(__dirname + 'src'),
query: {
presets: ['env']
}
}
]
}
注意:加入include
配置项会导致babel
不处理es6
语法,原因暂不明
- 4-3处理项目中的css
postcss-loader
autoprefixer是postcss-loader的一个子集
postcss
用在css-loader
和style-loader
之后,用在sass|less|stylus-loader
之前
module: {
rules: [{
test:/\.js$/,
include:__include,
exclude: __exclude,
use:{
loader: "babel-loader",
options:{
"presets": [
"es2015"
]
}
}
},
{
test: /\.css$/,
use: [
'style-loader',
{loader:'css-loader', options: {importLoaders: 1}},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
require('autoprefixer')
];
}
}
}
]
}
]
}
注意:
1. loader
的执行顺序从右向左
2. style-loader!css-loader?importLoaders=1!postcss-loader
3. ?importLoaders=1
是css-loader
后面的参数,意思是在css-loader后指定一个处理import样式的命令
- 4-4使用less和sass
sass-loader less-loader
npm install sass-loader node-sass webpack --save-dev
sass-loader
依赖node-sass
和webpack
{
test:/\.scss$/,
use:[
'style-loader',
{loader:'css-loader', options: {importLoaders: 1}},
{
loader:'sass-loader'
},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
require('autoprefixer')
];
}
}
}
]
}
- 4-5处理模板文件
html-loader
{
test:/\.html$/,
loader:'html-loader'
}
ejs-loader
{
test:/\.tpl$/,
loader:'ejs-loader'
}
- 4-6处理图片及其他文件
file-loader url-loader
用来处理图片
url-loader
和fileloader
差不多,但是可以指定limit
参数,当图片大小,小于limit
时由url-loader
处理成base64
,当图片大小大于imit时就由file-loader
处理成路径
image-webpack-loader用来压缩图片
在模版中使用相对路径图片的方法
${require('../../assets/bg.png')}
var htmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
module.exports = {
entry: {
"main": __dirname + '/src/script/main.js',
"a": __dirname + '/src/script/a.js',
"b": __dirname + '/src/script/b.js',
"c": __dirname + '/src/script/c.js',
},
output: {
filename: 'js/[name]-[hash].js',
path: __dirname + '/dist',
},
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
exclude: path.resolve(__dirname + 'node_modules'),
query: {
presets: ['env']
}
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:1
}
},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
require('autoprefixer')
];
}
}
}
]
}
]
},
plugins: [
new htmlWebpackPlugin({
filename: 'main.html',
template: 'index.html',
inject: false,
title: 'webpack is main.html',
date: new Date(),
excludeChunks: ['a', 'b']
}),
new htmlWebpackPlugin({
filename: 'a.html',
template: 'index.html',
inject: false,
title: 'webpack is a.html',
date: new Date(),
excludeChunks: ['b', 'c']
}),
new htmlWebpackPlugin({
filename: 'b.html',
template: 'index.html',
inject: false,
title: 'webpack is b.html',
date: new Date(),
excludeChunks: ['a', 'c']
}),
new htmlWebpackPlugin({
filename: 'c.html',
template: 'index.html',
inject: false,
title: 'webpack is c.html',
date: new Date(),
excludeChunks: ['a', 'b']
})
]
}
const htmlWebpackPlugin=require('html-webpack-plugin');
const path = require('path');
const __include=path.resolve(__dirname, "src");
const __exclude=path.resolve(__dirname, "node_modules");
module.exports={
entry:'./src/app.js',
output:{
path:__dirname+'/dist',
filename:'[name].bundle.js'
},
module:{
rules:[
{
test:/\.js$/,
include:__include,
exclude: __exclude,
use:{
loader: "babel-loader",
options:{
"presets": [
"es2015"
]
}
}
},
{
test:/\.html$/,
loader:'html-loader'
},
{
test: /\.css$/,
use: [
'style-loader',
{loader:'css-loader', options: {importLoaders: 1}},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
require('autoprefixer')
];
}
}
}
]
},
{
test:/\.scss$/,
use:[
'style-loader',
{loader:'css-loader', options: {importLoaders: 1}},
{
loader:'sass-loader'
},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [
require('autoprefixer')
];
}
}
}
]
}
]
},
plugins:[
new htmlWebpackPlugin({
title:'app',
inject:'body',
template:'index.html',
filename:'index.html',
minify:{
collapseWhitespace:true
}
})
]
}