webpack3中常用的一些操作

#webpack3中常用的一些操作

##新建配置文件
开始之前我们需要新建一些我们开始需要的文件,package.json文件, index.js入口文件,首先我

1
2
$ mkdir webpackdemo && yarn init && yarn webpack --dev
$ cd webacpkdemo && mkdir src && cd src && touch index.js

通过上面的命令行创建项目目录,并初始化配置文件,导入webpack, 并创建项目入口文件

在index.js中添加我们的内容,在这里我们引入lodash来作为本次的demo演示数据内容部分

1
$ yarn add lodash --dev

安装lodash后,进入index.js页面进行首次页面的操作

1
2
3
4
5
6
7
import _ from 'lodash'
function component(){
var element= document.createElement('div');
element.innerHtml = _.join(['你好','世界'],'');
return element
}
document.body.appendChild(component())

好我们的首次入口文件就已经添加好了,下面创建编译后的文件夹以及编译后的插入页面

1
$ mkdir dist && cd dist && touch index.html
1
2
3
4
5
6
7
8
9
10
11
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="bundle.js"></script>// bundle.js就是后面我们编译后的文件名
</body>
</html>

下面我们就行新webpack.config.js的编写,这个名字是webpack执行默认文件的名字,在执行webpack操作时会自动运行该文件。
在编写该文件之前我们尝试用最初的方法执行下编译

1
$ ./node_modules/.bin/webpack src/index.js dist/bundle.js

执行index.js并将生成bundle.js文件放入dist文件夹,这是最低级的办法,在工作中基本不会用到,下面则进行配置进行打包

1
2
3
4
5
6
7
8
const path = require('path')
module.epxort = {
entry:'./src/index.js',//入口文件
output:{
filename:'bundle.js',//编译后的文件名
path:path.resolve(__dirname, 'dist')//文件路径
}
}

在 package 文件中进行webpack的操作

1
2
3
"script":{
"build": "webpack"
}

运行命令行$ yarn run build,就会发现也会也会实现上面相同的功能

##打包其他类型文件
这部分内容,主要利用的是label的运用,在module模块下面的rules中添加我们需要打包的类型以及处理操作

####打包css文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const path = require('path')
module.epxort = {
entry:'./src/index.js',//入口文件
output:{
filename:'bundle.js',//编译后的文件名
path:path.resolve(__dirname, 'dist')//文件路径
},
module:{
rules:[
{
test:'\.css',//不要打包类型的匹配
use:[
'style-loader',
'css-loader'
]
//
}
]
}
}

这里注意,在webpack2 中-loader可省略的设置被取消掉了,必须要填写完全才可以运行,否则会报错,而执行操作的loader的相关功能,可以参展webpack官网进行配置

1
$ yarn add style-loader css-loader --dev

最后新建样式文件,并引入到index.js的文案里面

1
$ touch style.css
1
2
3
4
//style.css
.classname {
color:blue
}
1
2
3
4
5
6
7
8
9
import _ from 'lodash';
import './style.css';
function component(){
var element= document.createElement('div');
element.innerHtml = _.join(['你好','世界'],'');
element.classList.add('classname') //添加类名
return element
}
document.body.appendChild(component())

同上执行命令行运行就会发现生成两个文件,一个是js,一个是样式文件

###打包图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const path = require('path');
module.epxort = {
entry:'./src/index.js',//入口文件
output:{
filename:'bundle.js',//编译后的文件名
path:path.resolve(__dirname, 'dist')//文件路径
},
module:{
rules:[
{
test:/\.css/,//打包类型的匹配
use:[
'style-css',
'css-loader'
]
//
},
{
test:/\.(png|svg|jpg|gif)$/, //匹配图片
user:['file-loader']
}
]
}
}

同样在选择一张图片,并引入到入口文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
import _ from 'lodash';
import './style.css';
improt imgUrl from './img.png';
function component(){
var element= document.createElement('div');
var Img = new Image();
Img.src=imgUrl
element.innerHtml = _.join(['你好','世界'],'');
element.classList.add('classname') //添加类名
element.appendChild(Img)
return element
}
document.body.appendChild(component())
1
$ yarn add file-loader --dev

运行webpack 就会生成三中不同类型的文件,图片,样式,js文件。

###其他类型的打包
上面介绍了基础的一些文件的打包,其实还有可以打包的,比如说字体等,一些很多loader的配置,在这里就不一一举例了,可以自己全看文档进行学习

##输出管理

###多入口的添加
在一些时候我们需要进行多个入口文件,所以导致在编译的时候需要进行多入口的编译打包

1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path');
module.epxort = {
entry:{
app:'./src/index.js',
other:'./src/other.js'
},//入口文件
output:{
filename:'[name].bundle.js',//编译后的文件名
path:path.resolve(__dirname, 'dist')//文件路径
},
...
}

通过上面的操作就会进行多入口的操作,下面filename中的[name],就表示entry中入口文件的key,打包编译后就会生成app.bundle.js和other.bundle.js两个文件,同时我们在index.html中添加引入other.bundle.js的入口,当时文件一多这样就太麻烦了,所以我梦需要插件的协助,也是plugins等。

###Plugins
插件的使用和目的都是为了大大减轻我们在操作中的痛点,能够自动化的完成我们需要手动完成的事情

html-webpack-plugin

html-webpack-plugin插件的作用就是将编译好的文件自动插入到html文件中,并且可以设置一些属性操作

1
$ yarn add html-webpack-plugin --dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const HtmlWebpackPlugin = require('html-webpack-plugin')
...
{
...
'plugins':[
new HtmlWebpackPlugin({
title:'demo', //设置html文件title
filename:'index.html', //写入html文件的文字,默认index
template:'./src/index.html', //设置模板文件,即文件内容会跟模板一样
inject:'body' //编译文件添加到html中的位置,true和body都会放到body的底部
})
]
...
}

执行webpack命令行,自动生成文件并且插入到模板文件中.

clean-webpack-plugin

我们每执行一次文件发现都会新增一次文件,这就导致我们必须要手动每次删除文件夹,clean-webpack-plugin插件很好的结局了这个问题

1
$ yarn add clean-webpack-plugin --dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('clean-webpack-plugin')
...
{
...
'plugins':[
new HtmlWebpackPlugin({
title:'demo', //设置html文件title
filename:'index.html', //写入html文件的文字,默认index
template:'./src/index.html', //设置模板文件,即文件内容会跟模板一样
inject:'body' //编译文件添加到html中的位置,true和body都会放到body的底部
}),
new cleanWebpackPlugin(['dist']) //参数一位需要清理的目录
]
...
}

其实还有好多非常好用的插件,参展文档你可以慢慢学习各种差点的使用环境和方法

##开发环境
良好的开发环境能够大大增进开发的效率,事半工倍,在这介绍几个开发环境下常用到的配置项

devtool

在编译后的代码运行后,我们发现,开发工具里报错直接显示的是编译后的文件报错,这样的话导致我们很难定位到问题所在,加大了我们解决错误的时间和精力,所以我们添加devtool配置项能够定位到编译前文件的错误所在

1
2
3
4
5
...
{
devtool:'inline-source-map'// devtool的选项有很多,先选择报错最仔细的
}
...

这样子的话如果报错,问题很快找出位置并解决掉。

webpack-dev-server
故名思意,开发环境下设置的服务器,在开发过程中想要实时跟新自己的代码,查案效果的话,每次都需要编译是在太麻烦,webpack-dev-server就很好的解决了我们的痛点

1
$ yarn add webpack-dev-server --dev
1
2
3
4
5
...
devServer:{//监测代码是否改变,是则更新代码刷新页面
contentBase: path.resolve(__dirname, 'dist')//检测所在目录
}
...
1
2
3
4
// webpack.config.json
...
start : 'webpack-dev-server --open'; //--open打开浏览器,并开启node服务器,默认地址为localhost:8080
...

修改样式,保存,发现页面会自动刷新,但是在页面太多情况下,还是会造成刷新太慢太耗性能的问题,所以我们采用热加载的功能

模块热加载(HMR, Hot Module Replacement)

该功能很好的识别改动的模块,并只更新该模块的内容,效率大大提高同时,采用的是无刷新的更新方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
const webpack = require('webpack')
{
...
devserver:{
contentBase:path.resolve(__dirname, 'dist'),
hot:true, //设置启用HMR
hotOnly:true //报错不刷新页面
}
plugins:[
...,
new webpack.HotModuleReplacementPlugin(),//配合上面hot的设置才能生效
new webpack.NamedModulesPlugin()//打印时返回模块实际路径
]
...
}

>

webpack-dev-server 会为每个入口文件创建一个客户端脚本,这个脚本会监控该入口文件的依赖模块的更新,如果该入口文件编写了 HMR 处理函数,它就能接收依赖模块的更新,反之,更新会向上冒泡,直到客户端脚本仍没有处理函数的话,webpack-dev-server 会重新加载整个页面。如果入口文件本身发生了更新,因为向上会冒泡到客户端脚本,并且不存在 HMR 处理函数,所以会导致页面重载。

我们已经开启了 HMR 的功能,HMR 的接口已经暴露在 module.hot属性之下,我们只需要调用 HMR API即可实现热加载。当“被加载模块”发生改变时,依赖该模块的模块便能检测到改变并接收改变之后的模块。

下面我们在index页面进行module.hot的检测是否可以访问

1
2
3
4
5
6
7
//index.js文件地不添加
...
if(module.hot){
module.hot.accept('./print.js',function(){
console.log('更新更新!');
})
}

下面我们开启服务,发现修改样式等操作过后页面直接实现了热更新的功能

##生产环境
生产环境和开发环境的需求就不一样,基本上要求生成的文件体积小,没有多余的代码,那么我们怎么区别生产环境和开发环境呢。

设置变量进行辨别

1
$ webpack-p

这个命令行其实是一个缩写命令相当于 webpack–optimize-minimize–define process.env.NODE_ENV=”‘production’。

  1. 使用 UglifyJsPlugin,对代码进行压缩
  2. 运行 LoaderOptionsPlugin 插件,这个插件是用来迁移的,兼容处理
  3. 设置Node.js的变量,为后面做判断提供条件

在这个过程中为了能够在编译之前就能够识别这个环境变量,所以我们处理

1
$ yarn add cross-env --dev
1
2
3
4
5
6
//package.json
{
...
build:"cross-env NODE_ENV=production webpack -p"
...
}

下面我们在webpack.config.js文件中进行判断

1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path');
module.epxort = {
entry:{
app:'./src/index.js',
other:'./src/other.js'
},//入口文件
output:{
filename:process.env.NODE_ENV=='production'?'[name].[chunkhash].js':[name].bundle.js,//编译后的文件名
path:path.resolve(__dirname, 'dist')//文件路径
},
...
}

注意,在这里设置chunkhash时,我盟们不能使用HMR,否则在编译时会导致报错,这就导致了我们在开启开发环境和生产环境时导致要手动屏蔽一些代码,这样很麻烦,所以我盟可以编写连个环境的配置代码,将一些公用的放在一个文件进行公用,这里就用到了webpack-merge

webpack-merge

我们先新建三个文件,webpack.common.js用来设置在开发和生产环境中公用的配置项,webpack.pro.js配置生产环境配置项,webpack.dev.js则配置生产环境的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
output: {
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
title: 'webpack demo',
filename: 'index.html'
}),
new CleanWebpackPlugin(['dist'])
],
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
}
]
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//webpack.dev.js
cosnt path = require('path');
const Merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const webpack = require('webpack');
module.export = Merge(commonConfig,{
devtool:'cheap-module-eval-source-map',
devserver:{
contentBase:path.resolve(__dirname, 'dist'),
hot:true,
hotOnly:true
},
output: {
filename: '[name].bundle.js'
},
plugin:[
new webpack.defaultPlugin({
'process.env.NODE_NV':JSON.stringify('development') //设置node环境变量
})
new webpack.HotModuleReplacementPlugin()
new webpack.nameModulesPlugin()
]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//webpack.pro.js
cosnt path = require('path');
const Merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const webpack = require('webpack');
module.export = Merge(commonConfig,{
devtool:'cheap-module-source-map',
output: {
filename: '[name].[chunkhash].js'
},
plugin:[
new webpack.defaultPlugin({
'process.env.NODE_NV':JSON.stringify('development') //设置node环境变量
})
new webpack.optimize.UglifyJsPlugin()//压缩代码
]
})
1
2
3
4
5
6
7
8
9
10
//package.json
{
...
'script':{
...
'build:dev': webpack-dev-server --open --config webpack.dev.js,
'build:pro': webpack-dev-server --open --config webpack.pro.js
}
...
}

大功告成,这样子我们的两个简单的环境就算是配置完成了,需要更多的配置则可以查看官网倆选择性添加,添加配置的宗旨一定要牢记,是项目中需要什么东西我们才添加,而不是为了添加配置而配置。