webpack从零开始

570 阅读7分钟

webpack 是核心,webpack-cli是给我们提供了相关的命令,能够基于webpack相关命令进行编译和打包。

webpack webpack-cli是用来在开发时将代码编译打包的,打包出的代码部署在服务器上,在生产环境下发布。所以在生产环境下是不需要编译打包的,因此要安装在开发环境下。

一、安装

// 生成package.json
npm init -y 

// 安装在开发环境下
npm i webpack webpack-cli --save-dev

二、零配置使用

webpack默认会把当前项目src目录下的文件进行打包编译(零配置入口默认是index.js),编译到dist文件目录下(webpack编译代码的过程中支持Commonjs规范和ES6Module规范),未来部署到服务器上。

运行webpack

方法1:

npx webpack

方法2:

在package.json中配置可执行命令

{
  "scripts": {
    "serve":"webpack"
  },
}

==>  npm run serve

三、自定义webpack打包配置项

可创建两个js文件:webpack.config.js / webpackfile.js 文件名一定不能改变

  • 编写自定义的webpack配置项,以后webpack打包编译的时候是按照自己配置的内容进行打包编译处理的
  • 这个文件放在根目录下
  • 文件名:webpack.config.js 或 webpackfile.js (自己能识别)
  • webpack本身基于Node开发,所以配置项的模块处理规则参考CommonJs规范
const path = require('path')
module.exports = {
    // 设置编译模式, development(只合并,不压缩)/production(合并压缩,默认)
    mode:'production',

    // 设置编译的入口文件
    entry:'./src/main.js',

    // 设置编译出口文件
    output:{
        // 编译后的文件名[hash]编译时会随机在名字中生成唯一的哈希值,来保证每次编译出的文件是不一样的(防止浏览器缓存问题)
        filename:'bundle.[hash].min.js',
        // filename:'bundle.min.js',
        
        // 输出目录(绝对路径)
        path:path.resolve(__dirname,'build')
    }
}

四、plugin插件

1. 常用插件安装

此处安装了3个常用插件

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

2. 常用插件介绍

1)html-webpack-plugin

解决问题: 每次更改代码,重新编译后都需要手动去更改指定html页面中导入的js信息。此插件可以自动处理html的编译和导入文件。

注意:

a)将入口html页面(index.html)可以放在public目录下

b)index.html页面不再写引入js的代码

{
 // 在webpack中使用插件
    plugins:[
        // 配置指定的html页面模版(后期编译时会把编译好的资源文件自动导入到我们的页面模版中)
        new HtmlWebpackPlugin({
            // 模版路径
            template:'./public/index.html',
            // 编译后生成的文件名
            filename:'index.html',
            // 是否把编译的资源文件导入到页面中,设置hash值(清除强缓存,和output中hash值一样,保留一个hash即可)
            hash:true,
            // 把模版中的html代码也进行压缩编译(配置规则)项目常用
            minify:{
                // 删除标签之间的空格
                collapseWhitespace:true,
                // 去除注释
                removeComments:true,
                // 去除div属性的双引号,div="xxx"
                removeAttributeQuotes:true,
                // 去除空属性 <div class="aa" id=""></div> id整体会被删除
                removeEmptyAttributes:true
            }
        })
    ]
}

2). clean-webpack-plugin

解决问题:每次打包都把之前打包的内容清空

 plugins:[
        // 每次打包都把之前打包的清空
        new CleanWebpackPlugin()
    ]

3). webpack-dev-server

解决问题: 帮我们构建一个后台,这个后台的web服务可以做到:

  • 自动监听代码改变,改变自动编译
  • 自动打开浏览器渲染页面
  • 重新编译后自动刷新浏览器,看到最新效果 【除非配置项更改了需要自己重新执行,否则都是自动化处理】
module.exports = {
 // 配资dev-server
    devServer:{
        // 端口
        port:3000,
        // 开启gzip压缩
        compress:true,
        // 指定资源访问路径
        contentBase:path.resolve(__dirname,"build"),
        // 自动打开浏览器
        open:true,
        // 开启热更新
        hot:true,
        // proxy跨域代理
         proxy:{
            '/':'http://127.0.0.1:8888'
        }
    },
}

配资dev-server,编译后的结果放在计算机内存中,并不会像之前的webpack命令一样,把编译后的东西放到build下,dev-server仅仅是在开发模式下,随时编译并且预览的,项目要部署的时候,还是需要基于webpack编译打包的

"scripts": {
    "serve": "webpack-dev-server",
    "build": "webpack"
  },

所以在真实项目中有两种执行命令:

  • 开发下用serve,随时编译到内存,随时看效果,随时刷新
  • 开发完毕,执行build,把内容编译到build文件夹下,并上传至服务器
启用
{
 "scripts": {
    "serve": "webpack-dev-server"
  },
}
==> npm run serve

异常点:

webpack-dev-server启动如果报错:

Error: Cannot find module ‘webpack-cli/bin/config-yargs‘

原因:

由于webpack-cli版本4,删除了webpack-cli/bin/config-yargs文件,bin目录下没有config-yargs.js

解决方案

提示下载webpack-cli 3版本的依赖

卸载webpack-cli
npm uninstall webpack-cli

安装3版本webpack-cli
npm install webpack-cli@3.3 --save-dev

完美解决!

3. 模版导入资源文件

在模版中可以自己单独导入一些CSS/JS/图片等资源文件

  • 自己导入的文件不会受webpack编译的影响(不会和其他模块的文件编译在一起),所以有时候会单独导入一些公共资源文件
  • 有些资源类库不支持CommonJs规范和ES6Module规范,这样的资源文件只能在模版中手动导入,因为webpack也无法处理。

五、配置多入口的打包编译

module.exports = {
// 多入口
    entry:{
        index:'./src/main.js',
        login:'./src/login.js'
    },
     output:{
       // [name]多入口配置的属性名 index/login
        filename:'[name].[hash].min.js',
    },
}

配置多页面模版

const htmlPlugins = ['index','login'].map(item => {
    new HtmlWebpackPlugin({
        template:`./public/${item}.html`,
        filename:`${item}.html`,
        minify:{
            collapseWhitespace:true,
            removeComments:true,
            removeAttributeQuotes:true,
            removeEmptyAttributes:true
        }
    })
})

plugins:[
 ...htmlPlugins
]

打包后会发现每个html文件都会导入全部的入口js文件,这不是我们想要的,就可以配置chunks,chunks用于多入口的时候。

const htmlPlugins = ['index','login'].map(item => {
    new HtmlWebpackPlugin({
        template:`./public/${item}.html`,
        filename:`${item}.html`,
        chunks:[item],   // ***
        minify:{
            collapseWhitespace:true,
            removeComments:true,
            removeAttributeQuotes:true,
            removeEmptyAttributes:true
        }
    })
})

访问时默认进index页面

独立打包js文件

多个页面公共的部分我们可以独立打包出来。

以jquery为例,如果不想将jq合并在其他js中,想将jquery也单独打包编译成js文件,可以这么写:

 entry:{
        index:'./src/main.js',
        login:'./src/login.js',
        jquery:'jquery' // ***
    },

如果当前index和login页面都想引入jquery,可以这么配置

// 配置多页面模版
const htmlPlugins = ['index','login'].map(item => {
   return new HtmlWebpackPlugin({
        template:`./public/${item}.html`,
        filename:`${item}.html`,
        chunks:[item,'jquery'],  // ***
        minify:{
            collapseWhitespace:true,
            removeComments:true,
            removeAttributeQuotes:true,
            removeEmptyAttributes:true
        }
    })
})

实际项目中,我们需要先加载jquery,所以可以在chunks中调整顺序。

 chunks:['jquery', item],

非独立打包

不想独立打包,可以在对应的js文件,以CommonJs规范,或者ES6Module规范导入进来即可。

例如,在src/main.js文件导入jquery

const $ require('jquery')

六、CSS资源处理

1. 加载器loader--样式处理

1)安装 less 为例

npm install css-loader style-loader less less-loader autoprefixer postcss-loader --save-dev
  • css-loader: css加载器,处理@import/URL()语法

  • style-loader:样式加载器,把处理好的css插入到页面中(内嵌式)

  • autoprefixer postcss-loader:自动加前缀,处理兼容

    postcss-loader 搭配autoprefixer一起使用时,需要额外再配置一些信息。在根目录创建 postcss.config.js文件,使用它的语法包 postcss.config.js 配置

    module.exports = {
        plugins:[
            require('autoprefixer')
        ]
    }
    

    设置浏览器兼容 (package.json)

    github.com/browserslis… 中介绍了设置浏览器兼容的写法。

    {
       "browserslist":[
          "> 1%", // 包含了99%的浏览器
          "last 2 versions" // 兼容浏览器最近的两个版本
        ]
    }
    
  • less-loader:把less编译为css

安装sass:sass-loader node-sass

2)配置

module与plugins同级

// 配置weboack加载器 loader
    module:{
        // 设置规则和处理方式 默认执行顺序:从右到左,从下到上
        rules:[
            {
                // 匹配哪些文件基于处理
                test:/\.(css|less)$/i,
                use:[
                    "style-loader",
                    "css-loader",
                    "postcss-loader",
                    // "less-loader"
                // 不同写法,options可以添加额外的配置
                    {
                        loader:"less-loader",
                        options:{}
                    }
                ]
            }
        ]
    }

2. 抽离CSS :mini-css-extract-plugin 插件

将css内容抽离出来,形成单独文件(未压缩),用link形式导入进来,可以使用这个插件。

// 安装
npm install mini-css-extract-plugin --save-dev

// 导入 (webpack-config.js)
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// 使用
plugins:[
 new MiniCssExtractPlugin({
        filename:'[name].[hash].min.css'
    })
]

loader内容此时,要注意:不能再写style-loader了,要使用 MiniCssExtractPlugin自带的loading。这个loader能够将css分离出来,分离到我们指定的目录下(此时为filename的地址)。

 module:{
        // 设置规则和处理方式 默认执行顺序:从右到左,从下到上
        rules:[
            {
                // 匹配哪些文件基于处理
                test:/\.(css|less)$/i,
                use:[
                    // "style-loader", // *** 删除style-loader
                    MiniCssExtractPlugin.loader // ***
                ]
            }
        ]
    }

运行程序可以看到,css使用link引入

七、 设置优化项--压缩CSS/JS

1. 安装

npm install optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin terser-webpack-plugin --save-dev

2. 插件介绍

1) CSS压缩插件:optimize-css-assets-webpack-plugin

2) JS压缩插件(谷歌):uglifyjs-webpack-plugin

3) JS压缩插件 (常用):terser-webpack-plugin

使用

// 导入
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
// 配置webpack优化项
  optimization:{
      // 设置压缩方式
      minimizer:[
          // 压缩css
          new OptimizeCssAssetsWebpackPlugin(),
          // 压缩Js
          new UglifyjsWebpackPlugin({
              cache:true,// 是否使用缓存
              parallel:true, // 是否是并发编译
              sourceMap:true, //启动源码映射(方便调试)
          }),
          new TerserWebpackPlugin()

      ]
  }
}

Js压缩插件使用一个即可。

八、图片处理

项目中会用到图片的地方:

  • css设置背景图
  • js动态创建图片
  • html中直接写图片

1. 安装

npm install file-loader url-loader html-withimg-loader --save-dev

2. 加载器介绍

1)file-loader:编译图片的加载器

处理css,js中的图片,和字体图标的处理

    module:{
        // 设置规则和处理方式 默认执行顺序:从右到左,从下到上
        rules:[
             // 图片的处理
            {
                test: /\.(png|jpe?g|gif|ico|bmp)$/i,
                use: ['file-loader'],  // ***
                
                // 指定处理哪些目录下的
                include:path.resolve(__dirname,'src'), 
                
                // 忽略哪些目录下的不处理
                exclude: /node_modules/ 
            },

            // 字体图标的处理  编译图片的加载器
            {
                test: /\.(svg|eot|ttf|woff|woff2)$/i,
                use: ["file-loader"]  // ***
            },
        ]
    },

2)html-withimg-loader:处理html页面中的图片

html

  <img src="../src/static/image/icon.png" alt="">

webpack.config.js

    module:{
        // 设置规则和处理方式 默认执行顺序:从右到左,从下到上
        rules:[
            // 编译html页面中的图片,把其安照另外两种图片的处理机制处理
            {
                test: /\.html$/,
                use: ['html-withimg-loader']
            }
        ]
    },

3) url-loader:base64转换

url-loader在编译的时候,会把符合条件的图片进行base64,对于不符合条件的还是继续用file-loader处理。包括limit条件的。

module:{
 rules:[
   {
    test: /\.(png|jpe?g|gif|ico|bmp)$/i,
    // use: ['file-loader'],
    use :[{
        loader:'url-loader',
        options:{
            // 不超过200kb的用base64处理
            limit: 200 * 1024,
           // 在打包编译时,把图片都放在统一的images文件夹下
           // 指定名字
           name: 'images/[name],[hash].[ext]',
           esModule:false
        }
     }]
  }
 ]
}

3. 问题记录

问题:js动态创建图片,页面找不到图片地址问题.

// main.js
let image = new Image()
image.src = "./static/image/dog.jpg"
document.body.appendChild(image)

分析:

  • 如果地址是一个外网的绝对地址直接使用即可(编译后地址还是外网地址)
  • 如果需要设置的是相对地址,则需要基于require把图片导入进来再使用,否则编译后地址找不到。

因此,可以这么写

let A = require("./static/image/dog.jpg")
let image = new Image()
image.src = A
document.body.appendChild(image)

九、JS处理

基于babel实现ES6的转换

1. 安装

npm install babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime --save-dev

2. 加载器介绍

1)babel-loader:babel加载器

2)@babel/core:语法包

3)@babel/preset-env:语法包,将ES6转为ES5

3. 相关插件介绍

加载器可以将ES6转为ES5语法,但是有些ES6/ES7及以上无法直接转为ES5语法,因此需要引入一些插件。

1)@babel/plugin-proposal-decorators:类的装饰器

2) @babel/plugin-proposal-class-properties

这个插件可以转换,ES7中class的新语法。

// 举例:
class A{
  // ES6 
  constructor(){
    this.x = 10
  }

  // ES7中新增的语法,相当于this.x = 10
  x=10 
  
  // ES7
  static m = 10
  
}

ES7中我们可以在类里设置属性(基本类型)

3) @babel/plugin-transform-runtime

4. 配置

// JS处理
{
    test:/\.js$/i,
    use:[{
        loader:'babel-loader',
        options:{
            presets:[
                // 将ES6转为ES5
                "@babel/preset-env"
            ],
            // 基于插件处理ES6/ES7中class的特殊语法
            plugins:[
                // 类的装饰器
                ["@babel/plugin-proposal-decorators",{
                    "legacy":true
                }],
                // 类中设置属性的
                ["@babel/plugin-proposal-class-properties",{
                    "loose":true
                }],
                "@babel/plugin-transform-runtime"
            ]
        }
    }],
    include: path.resolve(__dirname, 'src'), // 指定处理哪些目录下的
    exclude: /node_modules/ // 忽略哪些目录下的不处理
}

持续更新...