webpack就是为打包而生的。

一、准备

学习webpack首先需要安装nodejs,并且作者也强调新版本的nodejs会提高webpack的打包速度。
安装好node后就可以在项目命令行中执行下面的代码

npm init

然后根据需要设置,也可以一直回车,当然如果都使用默认配置也可以

npm init -y

这样就可以在项目中生成package.json文件了。这样做的目的就是为了使项目符合node规范。


二、安装webpack

安装webpack有两种方式
1.全局安装webpack(不推荐)
在命令行中输入

npm install webpack webpack-cli -g

安装成功后可以通过命令 webpack -v查看是否安装成功
但是非常不推荐全局方式安装webpack,因为如果机器上有多项目用不同版本webpack就无法做到兼容。
全局卸载webpack

npm uninstall webpack webpack-cli -g 

2.项目内安装webpack(==推荐==)
命令行进入项目目录,执行下面命令

npm install webpack webpack-cli -D

这种方式安装成功后无法通过webpack -v查看webpack版本信息,因为默认node会查找全局下的webpack。这里就要通过npx命令查看。

npx webpack -v

npx命令会在当前项目的node_modules中找到我们安装的webpack

3.安装制定版本webpack

npm install webpack@4.16.0

可以通过webpack info 查看所有版本


三、开始打包

安装好webpack后就可以开始打包我们的项目代码了,如我们项目中有一个xxx.js文件。

npx webpack xxx.js 

这样就会将某一个js文件进行打包,打包后的文件就存放在项目根目录的dist文件夹中。这样就是webpack最基础的打包方式,使用的也是webpack默认的配置。如果想要更多的功能就要在webpack.config.js文件中进行配置


四、配置文件

在项目根目录中新建一个webpack.config.js文件。

const path = require('path');

module.exports = {
    mode: 'production', // 打包模式,production为生产模式,development为开发模式 开发模式下js不会被压缩
    // entry: 指定入口文件,
    entry: { // 打包输出两个文件
        home: './src/index.js',
        index: './src/index.js'
    },
    output: { // 输入配置
        publicPath:'http://cdn.com.cn',// 指定打包的文件前缀地址
        filename:'[name].js', // 打包后的文件名,可以使用placeholder占位符方式,[name]获取到entry中配置的名称
        path: path.resolve(__dirname, 'dist') // 打包好的文件夹,默认就是dist
    }
}

这就是最近本的打包配置,指定的打包模式,入口,输出配置等。
配置好后就可以通过

npx webpack

直接运行,不用再写具体文件,而是通过配置的入口去找到指定文件。
webpack.config.js 是webpack默认的配置文件,当然也可以手动修改
命令行执行如下面命令即可,npx webpack --config webpackconfig2.js

在平时开发的时候可能很少有人用到npx webpack这种命令进行打包,这是因为在package.json文件中对打包命令进行了简化,在scripts中添加如下代码

"scripts": {
   "bundle": "webpack"
},

这样改写后执行npm run bundle 相当于执行了webpack打包命令。
在scripts中使用webpack会优先在项目的node_modules中找webpack。

我们之前在安装webpack的时候,安装了一个webpack-cli,这个webpack-cli包的作用就是让我们可以在命令行中运行webpack命令(npx webpack等命令)。


五、loader

webpack的作用就是打包,但是webpack只能识别js文件,如果我们的js文件中引入了样式,或者尝试打包一个css文件、图片文件等,那么webpack就会报错。因为webpack不知道如何处理这样的文件。这时webpack就需要一个东西帮他处理,就是loader。
==loader就是帮助webpack打包不能识别的文件==

打包css

css-loader & style-loader

const path = require('path');

module.exports = {
    mode: ……
    entry: ……,
    output: ……,
    module:{
        rules:[
            {
                test: /\.css$/,
                use: ['style-loader','css-loader']
            }
        ]
    } 
}

想打包css文件就要安装style-loader和css-loader。

npm install style-loader css-loader -D

rules数组中就是很多打包规则,通过正则表达式匹配到css文件,然后告诉webpack一旦遇到css文件就使用style-loadercss-loader处理。
css-loader的作用是帮助webpack识别css。分析文件关系,然后合并。
style-loader的作用是得到css-loader分析出来的结果,将css挂载到html-header-style中。
==所以loader的调用是有顺序的,顺序是从后向前==,先执行css-loader再执行style-loader。

sass-loader
打包sass文件需要用到sass-loader。首先安装sass-loader和node-sass。
npm i sass-loader node-sass -D
然后改动一下rules

rules:[
    {
        test: /\.css$/,
        use: [
                'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 1,
                        modules: true // 默认false,引入的css属于全局的样式,开启true后,css变为模块化,引入css时可以通过style.xxx
                    }
                },
                'sass-loader'
            ]
    }
]

sass-loader的调用一定要在css-loader之前,所以要放到数组最后。但是如果一个scss文件中通过import了另一个scss文件,那么在打包第二个scss文件就会直接走到css-loader,导致打包失败。所以要在css-loaderoptions中加入importLoaders,表示走css-loader前还要走另外一个loader。

postcss-loader
postcss-loader可以自动添加css3前缀,需要新建一个postcss.config.js文件,并且要安装插件, npm i autoprefixed -D
将postcss-loader放到rules中sass-loader之前执行,也就是数组最后。
postcss.config.js

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

这样我们在打包css代码时,就会自动添加css3前缀。因为autoprefixer中内置了浏览器兼容表,默认>5%兼容。

打包文件

file-loader & url-loader
先安装npm i file-loader -D。打包图片文件如下

rules: [
    {
        test: /\.(jpg|png|gif)$/,
        use: {
            loader: 'file-loader',
            option: {
                name: '[name]_[hash].[ext]',
                outputPath: 'images/'
             }
        }
    }
]

name命名使用placeholder-占位符,不指定则打包后的文件是随机生成的名字。
对于特别小的图片,可以使用url-loader打包成base64格式放到js文件中。
先安装npm i url-loader -D,file-loader的功能url-loader同样也可以实现,通过配置大小限制,如果超过则使用传统方式打包,否则就使用base64方式打包,十分方便。

rules: [
    {
        test: /\.(png|jpg|gif)$/,
        use:{
            loader: 'url-loader',
            options:{
                name: '[name]_[hash].[ext]',
                outputPath: 'images/',
                limit: 2048 // 限制文件大小小于2048字节,使用base64方式打包
            }
        }
    }
]
打包es6

安装babel-loader 和 babel/core 包。
npm i babel-loader @babel/core -D
babel-loader是帮助webpack打包的工具
babel/core是babel的核心库。可以让babel识别js中的语法,将js语法转换为AST抽象语法树。
添加babel-loader分析js语法,打通babel和webpack,同时还需要借助其他babel模块才能将es6 转换为 es5。安装babel/preset-env
npm i @babel/preset-env -D
现在就可以将es6转换为es5了,但是一些方法在低版本的浏览器中依然不支持。还需要借助polyfill模块。
npm i @babel/polyfill -D
然后在业务代码中引入polyfill
import "@babel/polyfill"
然后改写webpack.config.js

rules: [
    {
        test: /\.js$/,
        exclude: /node_modules/, // 排除node_modules中的代码
        loader: 'babel-loader',
        options:{
            presets:[["@babel/preset-env"],{
                 targets:{
                     chrome: ">67" // 可以指定兼容浏览器版本
                 },
                 // 当使用polyfill的时候,会将所有特性都加入,而不是根据业务代码决定加什么,导致打包后的文件过大。所以通过如下配置解决这个问题,根据业务代码的需要将使用的特性打包进去
                 useBuiltIns: 'usage'
            }]
        }
    }
]

现在就可以正常打包es6的代码了。不过依然会有一个潜在的问题,就是polyfill再注入方法的时候通过全局变量的形式完成的,会污染全局环境,不适用组件打包。
解决办法:
npm i @babel/plugin-transform-runtime @babel/runtime @babel/runtime-corejs2 -D
在babel options中添加plugins配置。

rules: [
    {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options:{
            // 库代码使用此配置,解决polyfill的问题
            "plugins": [["@babel/plugin-transform-runtime"], {
                "corejs": 2,
                "helpers": true,
                "regenerator": true,
                "useESModules": false
            }]
        }
    }
]

补充:options内的配置可以单独放到.babelrc文件中


六、plugin

plugin类似一些框架中的生命周期函数,在打包特定的时间点触发去做一些事情。

HtmlWebpackPlugin
如html-webpack-plugin是在打包结束后。安装这个插件。
npm i html-webpack-plugin -D
HtmlWebpackPlugin插件会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html中,html中没有标签。也可以通过模版template,这样在生成html文件时会以配置的html文件为模版生成。

const path = require('path');

module.exports = {
    mode: ……
    entry: ……,
    output: ……,
    module: ……,
    plugins: [
        new HtmlWebpackPlugin({
            // 根据模版生成html文件
            template: 'src/index.html'
        })
    ] 
}

CleanWebpackPlugin
CleanWebpackPlugin插件会在打包前将dist目录删除(非官方)
cnpm i clean-webpack-plugin -D

module.exports = {
    mode: ……
    entry: ……,
    output: ……,
    module: ……,
    plugins: [
        // 重新打包先删除dist目录
        new CleanWebpackPlugin(['dist'])
    ] 
}

七、sourceMap

sourceMap 是一个映射关系,他知道dist目录下main.js文件出错位置,对应的是src下的index.js文件的位置。在webpack.config.js中添加devtool配置。

module.exports = {
    mode: ……
    devtool: 'none'
}

设置devtool: 'none',js报错只会提示在打包后的js文件中,在开发的时候定位错误就变得异常困难。
设置devtool: 'source-map',js报错会显示在原来的文件中,因为在打包后的目录中会生成一个map.js文件,里面存的就是映射关系。报错时通过映射关系找到原文件。
设置devtool: 'inline-source-map',inline这种方式不会生成map文件,而是生成base64字符串保存在打包后的js文件底部。
设置devtool: 'cheap-source-map',如不设置cheap,报错信息会具体到行列,导致映射关系复杂,耗费性能。加上cheap解决这个问题,同时不去处理第三方代码,只处理业务代码。到一些业务需要将我们自己写的模块或者第三方代码进行处理。那就要用到module。
设置devtool: 'cheap-module-source-map',设置module后解决cheap不处理第三方模块的问题。
设置devtool: 'cheap-module-eval-source-map',设置eval后,不会生成map文件,也不会有base64编码的字符串,而是改变代码的执行方式,通过sourceURL指向原文件。性能最好。
==开发环境最佳实践==:devtool: 'cheap-module-eval-source-map'
==生产环境最佳实践==:devtool: 'cheap-module-source-map'


八、devServer

每次修改后都要重新手动打包,这样非常浪费时间。有三种方式解决这个问题

1.在package.json中添加"watch": "webpack --watch"

scripts:{
   "watch": "webpack --watch"
}

打包的时候运行命令npm run watch
这时webpack就会监听要打包的文件,如果文件变化就会重新打包。

2.配置 devServer
安装dev-server 执行。npm i webpack-dev-server -D
在package.json中添加配置

scripts:{
   "start": "webpack-dev-server"
}

打包的时候运行命令npm run start
同时也可以在webpack.config.js中添加配置,

module.exports = {
    ……
    devServer: {
        contentBase: './dist', // 打包文件目录
        open: true, // 是否自动打开浏览器
        prot: 8080 // 启动服务端口
    }
}

==webpack-dev-server 打包后不会生成dist目录,而是在内存中。提升打包速度==
使用dev-server时可以搭配使用Hot Module Replacement (热模块替换)
引入HotModuleReplacementPlugin插件,可开启hmr功能,hmr可以热更js、css等代码。

module.exports = {
    ……
    devServer: {
        contentBase: './dist', // 打包文件目录
        open: true, // 是否自动打开浏览器
        prot: 8080, // 启动服务端口
        hot: true, // 开启hmr
        hotOnly: true // 阻止浏览器自动刷新
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
}

3.自己编写node服务
自己编写服务如server.js ,在package.json中配置server命令
"server": "node server.js",相当于在node中使用webpack
打包时执行命令npm run server即可。

九、总结

webpack基础用法就是这些,文中涉及到的内容足够满足一个小型项目的打包需求。基础用法熟练掌握后,可以通过阅读webpack官方文档进行更详细的打包配置。


巴斯光年
274 声望23 粉丝