webpack入门笔记

1,182 阅读7分钟

webpack的正确安装

  1. mkdir webpack-demo

  2. cd webpack-demo

  3. npm init

    // package.json
    {
      +"private":true// 代表该文件不会被发布到npm仓库上去
      -"main":"index.js",//因为只是自己使用,所以不需要向外暴露入口文件
    }
    
  4. npm install webpack webpack-cli -D

    在项目内安装webpack而不是在全局安装webpack,可以保证不同项目依赖不同版本的webpack而不会出错

  5. 此时终端输入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
  }
}

执行打包命令后,终端输出内容的含义

image-20191125170150927

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打包后的输出目录

屏幕快照 2019-11-25 下午5.03.21的副本 下午5.07.58

屏幕快照 2019-11-25 下午5.15.33

我们会发现,页面仍然是正常展示图片的,但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中

image-20191125171715079

打包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

image-20191125155004853

这时,我们去将index.css文件中的color变为blue时

屏幕快照 2019-11-25 下午3.50.59

浏览器会自动刷新,导致之前的状态消失,这不是我们要的,所以需要热模块更新

//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',
  }
}

image-20191125160500343

我们把数字点击到6,然后去修改b.js的代码,欢迎改为再见

image-20191125163705806

浏览器会重新刷新,之前的所有状态全部没有了。想要只刷改变的那一部分,应该这样写

//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