webpack的基础打包

129 阅读6分钟

一、认识webpack(本篇基于webpack5进行说明,与webpack4可能有些地方不太一样)

概念

事实上随着前端的快速发展,目前前端的开发已经变的越来越复杂了 比如也会使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,

通过sass、less等方式来编写css样式代码;

(1)比如开发过程中,我们还希望实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;

(2)比如开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化;

等等….

但是对于很多的前端开发者来说,并不需要思考这些问题,日常的开发中根本就没有面临这些问题:

这是因为目前前端开发我们通常都会直接使用三大框架来开发:Vue、React、Angular;

但是事实上,这三大框架的创建过程我们都是借助于脚手架(CLI)的;

事实上Vue-CLI、create-react-app、Angular-CLI都是基于webpack来帮助我们支持模块化、less、

TypeScript、打包优化等的;

webpack到底是什么?

官方的解释是 webpack is a static module bundler for modern JavaScript applications. 也就是说webpack是一个为了将现代js应用程序的静态资源模块化打包工具。 这句话我们可以了解到webpack是可以将代码打包成静态资源部署在静态服务器上,也支持模块化开发,例如ESModule、CommonJs、AMD、CMD等等

image.png 这张图也很明确的说了webpack打包的范畴。

webpack的安装

webpack的安装分为两个部分:webpack、webpack-cli

那他们是什么关系呢?

(1)执行webpack命令,会执行node_modules下的.bin目录下的webpack; (2)webpack在执行时是依赖webpack-cli的,如果没有安装就会报错; (3)而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程; (4)所以在安装webpack时,我们需要同时安装webpack-cli(第三方的脚手架事实上是没有使用webpack-cli的,而是类似于自己的vue-service-cli的东西)

大概步骤就是在命令行先执行webpack命令 然后执行过程中系统会在node_modules中.bin目录下查找webpack命令,然后依赖webpack-cli,最后webpack打包程序。

webpack的默认打包

我们可以通过webpack进行打包,之后运行打包之后的代码

在目录下直接执行 webpack 命令

生成一个dist文件夹,里面存放一个main.js的文件,就是我们打包之后的文件:

这个文件中的代码被压缩和丑化了;

另外我们发现代码中依然存在ES6的语法,比如箭头函数、const等,这是因为默认情况下webpack并不清楚我们打包后的文件是否需要转成ES5之前的语法,后续我们需要通过babel来进行转换和设置;

我们发现是可以正常进行打包的,但是有一个问题,webpack是如何确定我们的入口的呢?

事实上,当我们运行webpack时,webpack会查找当前目录下的 src/index.js作为入口;

所以,如果当前项目中没有存在src/index.js文件,那么会报错;

当然,我们也可以通过配置来指定入口和出口

npx webpack --entry ./src/main.js --output-path ./build

创建局部webpack

前面我们创建webpack打包用的是全局webpack,但是在真实的开发中不同的项目需要不同的webpack,所以下面我们来创建局部的webpack,步骤如下:

第一步,在命令行下用npm init -y创建package.json,用于管理项目的信息,相关的库文件等等;

第二步,在命令行下(也就是在你的目录结构下)使用npm install webpack webpack-cli -D

第三步,使用局部的webpacknpx webpack

第四部,在package.json下的scripts里面加入如下代码: "scripts": { "build": "webpack" }

第五步,使用npm run build来打包程序

我们先创建文件夹格式 image.png 在src文件夹下创建js文件,里面有两个format.js和math.js,

format.js 使用CommonJs来进行导出,

const format = function() {
    return '$96.00'
}
module.exports = {
    format
}

math.js

使用esmodule导出,(嘻嘻)

export function add(a, b) {
    return a + b
}

我们在index.js中引入这两个js文件,

index.js

// es的导入与导出
import { add } from './js/math'
// common.js的导入与导出
const { format } = require('./js/format')

console.log(add(10, 20));
console.log(format());

下面我们来对webpack的进行简单的配置

配置webpack的文件名称是webpack.config.js 我们先对js文件进行打包

js的打包

在打包js 的过程中,会根据配置文件选择入口,从入口开始,会形成一系列的依赖图,然后从依赖图中寻找其他对应的文件。


const path = require('path')
module.exports = {
//入口文件
     entry:'./src/index.js',
  //生成的文件
  output:{
          //文件路径
          path:path.resolve(__dirname,'./build')
          //文件名
          filename:'bundle.js'
     },   
}

然后我们开始将打包后的bundle.js引入到html里面,然后运行,得出结果。

image.png

css,less等的打包

我们先对css,less,sass,scss等样式来进行打包处理,尤其是less预处理的这类语言最终都会被转换成css文件形式,最后css进行渲染页面.

loader 的配置方式

配置方式表示的意思是在我们的webpack.config.js文件中写明配置信息:

module.rules中允许我们配置多个loader(因为我们也会继续使用其他的loader,来完成其他文件的加载);

这种方式可以更好的表示loader的配置,也方便后期的维护,同时也让你对各个Loader有一个全局的概览;

module.rules的配置如下:

rules属性对应的值是一个数组:[Rule]

数组中存放的是一个个的Rule,Rule是一个对象,对象中可以设置多个属性:

test属性:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;

use属性:对应的值时一个数组:[UseEntry]

UseEntry是一个对象,可以通过对象的属性来设置一些其他属性

loader:必须有一个 loader属性,对应的值是一个字符串;

options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;

query:目前已经使用options来替代;

传递字符串(如:use: [ 'style-loader' ])是 loader 属性的简写方式(如:use: [ { loader: 'style-loader'} ]);

loader属性: Rule.use: [ { loader } ] 的简写

image.png 在js文件夹下创建element.js用来创建dom结构 代码如下:

// import '../css/style.css'
import '../css/title.less'
const divEl = document.createElement('div')
divEl.className = 'title'
divEl.innerText = '你好,webpack'
document.body.appendChild(divEl)

在src文件下创建css文件夹,创建两个style.css

style.css

  .title {
    color: red;
    font-size: 20px;
    font-weight: 800;
}

title.less

@bgColor:red;
@textDecoration:pink;

.title{
    background-color: @bgColor;
    color:@textDecoration;
}
const path = require('path')
module.exports = {
//入口文件
     entry:'./src/index.js',
  //生成的文件
  output:{
          //文件路径
          path:path.resolve(__dirname,'./build')
          //文件名
          filename:'bundle.js'
     },
     module:{
         rules:[
           {
            test:/\.css$/,
   //当然你需要先下载css-loader,由于是开发依赖,所以使用`npm install css-loader -D`来进行安装
            use:[
            'style-loader',
            'css-loader'
            ]
           },
           {
              test:/\.less$/,
   //当然你需要先下载less-loader,由于是开发依赖,所以使用`npm install css-loader -D`来进行安装
            use:[
            'style-loader',
            'css-loader',
            'less-loader'
            ]
           }
         ]
     }
}

还有为什么上面会加入style-loader这个loader?

一方面是我们将css或者less解析完成以后,此时并没有插入到页面中,所以我们需要配置style-loader,从而让样式生效;至于放的位置,这也是有顺序的,这是因为loader 的加载顺序是从上到下,从右到左,从 以上只是展示了css和less的配置打包,sass,scss,styuls的配置都是一样的 然后npm run build 进行打包,然后运行 image.png

接下来我们对PostCSS进行配置

那么什么是postCss? PostCSS是一个通过JavaScript来转换样式的工具;

这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置;

尤其是浏览器前缀,开发的时候有时候为了兼容需要用的到 比如下面的代码

image.png 但是实现这些功能,我们需要借助于PostCSS对应的插件

先来安装postcss-laoder 使用命令npm install postcss-loader -D

在webpack.config.js文件里面加入postcss的loader配置

const path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './build'),
        filename: 'bundle.js'
    },
    module: {
        // 规则
        rules: [{
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                {
                    loader: 'postcss-loader',
                    options: {
                        postcssOptions: {
                            plugins: [
                                require('autoprefixer')
                            ]
                        }
                    }
                }
            ]
        }, {
            test: /\.less$/,
            use: [
                'style-loader',
                'css-loader',
                'less-loader'
            ]
        }, ]
    }

}

或者还有一种配置的方式 此时的webpack.config.js文件中就不需要把css的属性加入浏览器的前缀

const path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './build'),
        filename: 'bundle.js'
    },
    module: {
        // 规则
        rules: [{
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-laoder'
                /*{
                    loader: 'postcss-loader',
                    options: {
                        postcssOptions: {
                            plugins: [
                                require('autoprefixer')
                            ]
                        }
                    }
                }*/
            ]
        }, {
            test: /\.less$/,
            use: [
                'style-loader',
                'css-loader',
                'less-loader'
            ]
        }, ]
    }

}

那就是再创建一个新的文件postcss.config.js将plugins的配置放在另一个文件中,然后用CommonJs导出

module.exports = {
   plugins:[
       //require('autoprefixer')
       require('postcss-preset-env')
   ]
}

图片或者文件的打包

file-loader或者img-loader打包图片

element.js中加入图片盒子

image.png

import '../css/index.css'
// import '../css/index.less'
import '../css/image.css'
const divEl = document.createElement('div')
const div2 = document.createElement('div')
divEl.className = 'title'

divEl.innerText = '你好,webpack'
div2.className = 'image-bg'
document.body.appendChild(divEl)
document.body.appendChild(div2)

image.css

.image-bg {
    background-image: url('../img/1.png');
    background-size: cover;
    width: 200px;
    height: 200px;
}

webpack.config.js的配置

   {
         test: /\.(png|jpg|gif|svg|jpeg)$/,
         use: 'img-loader'
   }

但是上面的打包起来有些不方便,就是在图片比较多的时候,容易找不对对应的打包图片,也就是文件名不容易看,例如我按照上面的方式打包以后的图片会显示在build 文件夹下,比较乱。

image.png

我们这个时候就需要图片命名打包,

在图片的配置当中使用file-loader来进行图片命名

使用hash函数来对图片名进行不重复命名

{
  test:/\.(png|jpg|gif|svg|jpeg)$/,
  use:{
     loader:'file-loader',
     options:{
       //指定图片的存放路径
        outputPath:'img',
        name:'[name]_[hash:6].[ext]'
     }
  }
}

当执行完npm run build的时候,build文件夹下回出现img,img文件夹下就是所保存的图片以及文件

image.png

url-loader来打包小图片

file-loader和url-loader相比,后者可以将较小的文件,转换成base64URI 当我们将项目打包以后,那么打包以后的文件就是js文件,图片文件,html文件,然后部署到服务器上,当我们网络请求服务的时候,会下载这些文件,尤其是有图片的时候,会不断的发送请求来展示图片,但是当图片数量比较多的时候,对服务器性能是一个不小的考验,所以这个时候我们一般将小的图片变成精灵图,还有小字体图,来优化性能;还有一方面我们可以使用base64URI来将图片转码,然后在请求过程中将码在转换成图片,这是一种高并发的优化;

{
            test: /\.(png|jpg|gif|svg|jpeg)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: 'img/[name]_[hash:6].[ext]',
                    // 表示小于100kb的用url-loader的base64URI编码
                    limit: 100 * 1024
                }
               }
 }

图片编码之webpack5的asset方法

webpack5开始,我们可也直接使用资源模块类型asset module type 来代替上面的这些loader配置

{
            test: /\.(png|jpg|gif|svg|jpeg)$/,
            type:'asset',
            //生成的文件名
            generator:{
               filename:'img/[name]_[hash:6][ext]'
            },
            parser:{
              ///图片的大小限制
              dataUrlCondition:{
                 maxSize:100 * 1024
              }
            }
 }

字体的打包

首先我这里使用的是阿里云的字体图标库,可以下载以后引入到element.js文件中

{
            test: /\.(eot|ttf|woff2?)$/,

            use: {
                loader: 'file-loader',
                options: {
                    name: 'font/[name]_[hash:6].[ext]'
                }
            }
        }

插件的安装与配置

但是按照上面的打包方式还是稍微有点问题

那就是每次在重新打包的新的东西的时候,我们需要先删除之前的打包的文件,不然的话每次打包的文件都会重叠;也就是每次打包新的之前把旧的build文件删除掉;我这个时候我们需要一个插件CleanWebpackPlugin; 插件是贯穿整个webpack的周期的,loader只是用于特定的类型进行转化,plugin可以用于执行更加广泛的任务,比如打包优化,资源管理,环境变量的注入;

CleanWebpackPlugin插件的使用

插件必须是我们手动进行导入;

const path = require('path')
    // 导入cleanwebpackPlugin插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './build'),
        filename: 'bundle.js'
    },
    module: {
        // 规则
        rules: [{
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader'
                // 'css-loader', {
                //     loader: 'postcss-loader',
                //     options: {
                //         postcssOptions: {
                //             plugins: [
                //                 require('autoprefixer')
                //             ]
                //         }
                //     }
                // }
            ]
        }, {
            test: /\.less$/,
            use: [
                'style-loader',
                'css-loader',
                'less-loader'
            ]
        }, {
            test: /\.(png|jpg|gif|svg|jpeg)$/,
            use: {
                loader: 'file-loader',
                options: {
                    outputPath: 'img',
                    name: '[name]_[hash:6].[ext]'
                }
            }
        }, {
            test: /\.(png|jpg|gif|svg|jpeg)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: 'img/[name]_[hash:6].[ext]',
                    // 表示小于100kb的用url-loader的base64URI编码
                    limit: 100 * 1024
                }
            }
        }, {
            test: /\.(png|jpg|gif|svg|jpeg)$/,
            type: "asset",
            generator: {
                filename: 'img/[name]_[hash:6][ext]'
            },
            parser: {
                dataUrlCondition: {
                    maxSize: 100 * 1024
                }
            }
        }, {
            test: /\.(eot|ttf|woff2?)$/,

            use: {
                loader: 'file-loader',
                options: {
                    name: 'font/[name]_[hash:6].[ext]'
                }
            }
        }]
    },
    plugins: [
        // 一个一个的插件对象
        // 使用这个插件
        new CleanWebpackPlugin()
    ]


}

HtmlwebpackPlugin

接下来我们使用HtmlwebpackPlugin来自动生入口html文件,也就是我们这里的index.html

plugins:[
    new CleanWebpackPlugin(),
   new HtmlWebpackPlugin({
      
   })
]

CopyWebpackPlugin

new CopyWebpackPlugin({
            patterns: [{
                from: 'src',
                to: './',
                globOptions: {
                    ignore: [
                        '**/index.html'
                    ]
                }
            }]
        })

CopyWebpackPlugin用来将文件复制到build文件夹下

image.png

mode和devtool配置

mode不只是帮助我们配置文件,在开发阶段会帮助我们配置默认的东西;

module.exports = {
    // 选择模式,开发模式development(开发阶段)还是生产模式production(准备打包上线的时候)
    mode: 'development',
    // 映射文件,建立js文件,方便调试错误
    devtool: 'source-map',
    ...
 }

好了,以上就是webpack的一些简易配置。