webpack的正确安装
-
mkdir webpack-demo
-
cd webpack-demo
-
npm init
// package.json { +"private":true// 代表该文件不会被发布到npm仓库上去 -"main":"index.js",//因为只是自己使用,所以不需要向外暴露入口文件 }
-
npm install webpack webpack-cli -D
在项目内安装webpack而不是在全局安装webpack,可以保证不同项目依赖不同版本的webpack而不会出错
-
此时终端输入
webpack
,会提示找不到webpack ,这是因为node会去全局的目录寻找webpack我们可以使用
npx webpack
,npx会帮助我们在当前文件夹内寻找相应的安装包
webpack配置
在没有写webpack的配置文件时,执行npx webpack index.js
,webpack会根据默认配置帮我们进行打包
但当我们新建一个webpack.config.js
文件时,webpack则会根据我们写的配置文件进行打包
//webpack.config.js
const path. = require('path');
module.exports = {
mode:'production',//打包的模式。生产环境下会压缩代码,development则不糊压缩
entry:'./index.js',// 入口文件
output:{
filename:'bundle.js',//打包后的文件名
path:path.resolve(__dirname,'bundle')// 打包后的 文件目录
}
}
指定webpack以哪个配置文件来打包
npx webpack --config 文件名.js
简化打包的命令行指令
//package.json
{
"scripts":{
"bundle":"webpack"//之后执行命令行npm run bundle === npx webpack
}
}
执行打包命令后,终端输出内容的含义
Hash: 本次打包对应的唯一一个hash值
Version: 本次打包对应的webpack的版本号
Time: 打包的时间
Asset: 打包出的文件名
Size: 打包后的文件大小
Chunks: 存放文件的ID值,如果bundle.js 跟a文件有关联,则会在此处记录bundle文件和a文件的ID值
Chunk Name: 存放文件本身与关联文件的名字
Entrypoint: 入口文件是哪一个
列举打包的源文件
webpack打包图片
//index.js
import avatar from './avatar.jpg'
console.log(avatar)//avatar132425454.jpg
webpack默认是可以打包j s文件的,但对于怎么打包图片确实不知道的,所以我们需要在配置文件中告诉webpack怎么去打包图片,这就需要用到loader,loader就是一个打包的方案
npm install file-loader
//webpack.config.js
module.exports = {
module:{
rules:[
{
test:/\.jpg$/,
use:{
loader:'file-loader'
}
}
]
}
}
打包完后,会发现图片的名字改变了,如果想让图片的名字不改变,我们可以给file-loader添加参数
// webpack.config.js
module.exports = {
module:{
rules:[
{
test:/\.(jpg|png|gif)$/ //对以jpg,png,gif结尾的文件进行打包
use:{
loader:'file-loader',
options:{
name:'[name]_[hash].[ext]',
//[name]是源文件的名字,[hash]是哈希值,不需要可以删除,[ext]是源文件的后缀名
outputPath:'images/' //打包到哪个目录文件下
}
}
}
]
}
}
与file-loader相似的还有url-loader
npm install url-loader -D
//webpack.config.js
module.exports = {
module:{
rules:[
{
test:/\.(jpg|png|gif)$/
use:{
loader:'url-loader',
options:{
name:'[name].[ext]',
outputPath:'images/'
}
}
}
]
}
}
对比file-loader和url-loader打包后的输出目录
我们会发现,页面仍然是正常展示图片的,但file-loader打包后,图片的一个单独的文件放在输出目录中,而url-loader打包后,则是将图片变为base64格式存放在bundle.js文件中
url-loader的优点:减少了加载图片的网络请求,缺点,图片过大时,会影响js文件的加载速度
解决,设置一个图片大小的阀值 limit
options:{
name:'[name].[ext]',
outputPath:'images/',
limit:2048 //单位字节
}
当图片大小大于2048字节时,就会输出为单独的文件,小于2048字节时,就会以base64格式存放在bundle.js中
loader打包静态样式
//index.js
import './index.css'
当我们在js文件中引入c s s文件时,webpack打包时会提示错误,这时候就需要loader来帮忙了
npm install style-loader css-loader -D
//webpack.config.js
module.exports = {
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
}
}
那css-loader和style-loader的作用分别是什么呢
css-loader负责处理css文件之间的关系
/*a.css*/
body{
background-color:blue
}
/*b.css*/
import './a.css'
//index.js
import './b.css'
style-loader则是将解析完毕的css挂载到header中
打包scss文件
npm install style-loader css-loader sass-loader node-sass
module.exports = {
module:{
rules:[
{
test:/.scss$/,
use:['style-loader','css-loader','sass-loader'],
}
]
}
}
注意loader是从下到上,从右到左到顺序,即是有顺序的
自动添加css3的前缀
npm install postcss-loader autoprefixer -D
在根目录下新建文件postcss.config.js
//postcss.config.js
module.exports = {
plugins:[
require('autoprefixer')
]
}
//webpack.config.js
module.exports = {
module:{
rules:[
{
test:/\scss$/,
use:['style-loader','css-loader','sass-loader','postcss-loader']
}
]
}
}
打包样式的细节补充
module.exports = {
module:{
rules:[
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:2
}
},
'sass-loader',
'postcss-loader'
]
}
}
importLoaders:2指引入的sass文件也要去执行posts-loader和sass-loader
/*index.scss*/
import './a.scss'
body{
#root{
color:red;
}
}
/*a.scss*/
...
//index.js
import './index.scss'
webpack去解析index.scss时会经过postcss-loader,sass-loader,css-loader,接着会去解析引入的a.scss,如果我们希望引入的的a.scss也要经过postcss-loader,sass-loader时,就要在cs s-loader的options参数中加上importloaders:2
css打包的模块化
/*a.scss*/
body{
.title{
color:red
}
}
// index.js
import './a.scss'
当我全局引入a.scss文件时,就会使得全局的title类名的元素文字颜色都变为红色。
于是我们要引入一个css模块化概念
//webpack.config.js
module.exports = {
module:{
rules:[
{
test:/\.scss$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:2,
modules:true,// 变为模块化打包
}
},
'sass-loader',
'postcss-loader'
]
}
]
}
}
//index.js
import style from './a.scss'
var span = document.createElement('span')
span.innerText='你好'
span.classList.add(style.title)//样式只会影响这个元素,而其他类名为title元素则不会受影响
var root = document.getElementById('root')
root.append(span)
使用plugins打包更加便捷
自动生成html文件
每次打包文件,我们都需要在dist文件夹中新建一个html文件,然后引入打包后的j s文件,这是非常麻烦的
现在我们推荐使用
html-webpack-plugin
使得打包更便捷
npm install -D htm-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins:[
new HtmlWebpaclPlugin()
]
}
htmlwebpackplugin会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中
打包后,我们会发现webpack自动生成的html文件缺少一个id为root的div
如果我们希望webpack打包后自动生成一个id为root的div,我们可以给HtmlWebpackPlugin一个模版
//webpack.config.js
module.exports = {
plugins:[
new HtmlWebpackPlugin({
template:'src/index.html'//模版文件
})
]
}
plugin可以在webpack运行到某个时刻的时候,帮你做一些事情
在重新打包时,将dist目录先删除,再打包
npm install -D clean-webpack-plugin
//webpack.config.js
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
plugins:[
new HtmlWebpackPlugin({
template:'src/index.html'
}),
new CleanWebpackPlugin()
]
}
Entry与Output的基本配置
module.exports = {
entry:{
key1:path1,
key2:path2
}
output:{
filename:xxx.js
path:path.resolve(__dirname,'dist')
}
}
key1与key2,代表打包输出的文件名,path1与path2代表要打包文件的路径
filename也代表打包输出的文件名,单filename与key冲突时,会报错,当有两个key,而filename只有一个时,也会报错,建议修改如下
module.exports = {
entry:{
main:'./src/main.js',
other:'./scr/other.js'
},
output:{
filename:'[name].js'//用[name]作为占位符,这样打包输出的文件名就会是entry中的key值
path:path.resolve(__dirname,'dist')
}
}
sourceMap
有时代码写错了,控制台报错,会指出打包后的文件的哪一行代码错误,但我们想知道的是src目录下的哪个文件的哪行代码错误。这时候就需要
sourceMap
module.exports = {
devtool:'source-map'
}
sourceMap其实就是一个映射关系
devtool | build | rebuild | production | quality |
---|---|---|---|---|
(none) | fastest | fastest | yes | bundled code |
eval | fastest | fastest | no | generated code |
cheap-eval-source-map | fast | faster | no | transformed code (lines only) |
cheap-module-eval-source-map | slow | faster | no | original source (lines only) |
eval-source-map | slowest | fast | no | original source |
cheap-source-map | fast | slow | yes | transformed code (lines only) |
cheap-module-source-map | slow | slower | yes | original source (lines only) |
inline-cheap-source-map | fast | slow | no | transformed code (lines only) |
inline-cheap-module-source-map | slow | slower | no | original source (lines only) |
source-map | slowest | slowest | yes | original source |
inline-source-map | slowest | slowest | no | original source |
hidden-source-map | slowest | slowest | yes | original source |
nosources-source-map | slowest | slowest | yes | without source content |
开发环境建议这么配置
module.exports = {
devtool:'cheap-module-eval-source-map'
}
生产环境
module.exports = {
devtool:'cheap-module-source-map'
}
使用WebpackDevServer提升开发效率
每次改完代码,都要打包后,再去刷新浏览器,这样比较麻烦
我们希望可以改完代码后,webpack自动打包,然后自动刷新浏览器
想实现这样的功能,有两个方法
1
{
"scripts":{
"watch":"webpack --watch"//webpack会去监听要打包的文件,当文件发生变化,就会自动去打包
}
}
2
npm install -D webpack-dev-server
借助WebpackDevServer
来监听文件变化并打包,自动打开并刷新浏览器
//webpack.config.js
module.exports = {
devServer:{
contentBase:'./dist'//服务器的根路径就是在dist目录下,
open:true,//会自动打开浏览器,并去访问相应的服务器
proxy:{
'/api','http://localhost:3000'//支持跨域,当访问api时,会转发到localhost:3000
}
}
}
{
"scripts":{
"start":'webpack-dev-server'
}
}
使用webpack-dev-server 时。你会发现,现在没有dist目录了 ,这是因为webpack-dev-server将打包后后的文件放在了电脑的缓存中
热模块替换
当我们改变样式代码时,浏览器会刷新,之前的状态就会消失,这不是我们想要的,我们希望,我们改变样式代码时,浏览器不会自动刷新
// index.js
import './index.css'
var btn = document.createElement('div')
btn.innerText = '点击我'
btn.onclick = ()=>{
var div = document.createElement('div')
div.classList.add('item')
div.innerText='item'
document.body.appendChild(div)
}
.item:nth-of-type(odd){
color:red
}
点击几次,就会出现多个item
这时,我们去将index.css文件中的color变为blue时
浏览器会自动刷新,导致之前的状态消失,这不是我们要的,所以需要热模块更新
//webpack.config.js
const webpack = require('webpack')
module.exports = {
devServer:{
contentBase:'./dist',
open:true,
port:8080,
hot:true,//热模块替换
hotOnly:true,//即使html不生效,浏览器也不自动刷新,可以不加
},
plugins:[
new webpack.HotModuleReplacementPlugin()
]
}
js代码的热更新
//a.js
var num = 0;
function number(){
var btn = document.createElement('div')
btn.setAttribute('id','number')
btn.innerText = num;
btn.onclick = ()=>{
num++1;
btn.innerText = num
}
document.body.appendChild(btn)
}
export default number;
//b.js
function show (){
var div = document.createElement('div')
div.innerText = '欢迎'
document.body.appendChild(div)
}
export default show
//index.js
import number from './a'
import show from './b'
show()
number()
//webpack.config.js
module.exports = {
devServer:{
hot:true,
contentBase:'./dist',
}
}
我们把数字点击到6,然后去修改b.js的代码,欢迎改为再见
浏览器会重新刷新,之前的所有状态全部没有了。想要只刷改变的那一部分,应该这样写
//index.js
import number from './a'
import show from './b'
show()
number()
if(module.hot){// 监测热更新,
module.hot.accept('./a',()=>{
//先删除之前的dom
ducument.body.removeChild(document.getElementById('number'))
//再重新执行一次
number()
})
}
结语
如果觉得文章不错,请点个赞,有错漏处,还请各位看官指正
如果要转载,请注明出处
作者:胡志武
时间:2019/11/25