Webpack4 那点儿东西

13,330 阅读6分钟

What

近几年,构建对于前端开发来说是一个很重要的名词,它给前端开发注入了很大的活力,解放了很多生产力。而webpack在前端项目中起了不可小觑的作用,它可以将我们所用的各种浏览器不认识的代码比如es6,es7,sass,less等转换为浏览器认识的语言,可以对文件进行压缩合并,代码进行分割,模块合并。。。

Why

昨天翻看我司的项目,虽然各个项目用的都是webpack,但是哪个版本的都有,webpack1,webpack2,webpack3应有具有。很多插件随着版本的升级是会有问题或者是不能再用的,因此对于webpack的配置还是要不断更新,不断学习的。所以我觉得有必要总结一下,正巧webpack于2018年2月25日正式发布v4.0.0版本,代号legato,所以本文基于最新的webpack4.0进行讲解常规用法。不过第一步,我觉得还是有必要讲下webpack4的Update Log中比较重要的几条。

Update

  1. 环境支持: 官方宣布不再支持Node 4, Node 6,使用的是v8 5.0版本,支持93%的ES6语法。因为webpack4使用了很多JS新的语法,它们在新版本的 v8 里经过了优化。
  2. 0配置 受Parcel打包工具启发,尽可能的让开发者运行项目的成本变低。webpack4不再强制需要 webpack.config.js 作为打包的入口配置文件了,它默认的入口为'./src/'和默认出口'./dist',这对于小项目来说确实是一件不错的事情。
  3. Mode webpack需要设置mode属性,可选 development 或 production。
"scripts": {
  "dev": "webpack --mode development",
  "build": "webpack --mode production"
}

development模式特性:

a.浏览器调试工具
b.注释、开发阶段的详细错误日志和提示
c.快速和优化的增量构建机制

production模式特性:

a.开启所有的优化代码
b.更小的bundle大小
c.去除掉只在开发阶段运行的代码
d.Scope hoisting和Tree-shaking
  1. 插件变化 webpack4删除了CommonsChunkPlugin插件,它使用内置API optimization.splitChunks 和 optimization.runtimeChunk,即webpack会默认为你生成共享的代码块。
  2. 开箱即用WebAssembly(笔者暂时未使用)

Use

  1. 配置项
  • Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
  • Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  • Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
  • Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
  • Plugin:扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
  • Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。
  1. 启动命令 执行webpack --mode development 会去全局找webpack包,如果没有安装的话会告诉你 bash: webapck: command not found。解决方案:
  • 使用npx 即 npx webpack development( npm 5.2.0版本支持的一个工具)详细介绍
  • 在package.json中配置scripts
"scripts": {
    "build": "webpack --mode development",
    "dev": "webpack-dev-server --open --mode development"
  },
  1. 上述在scripts我们已经配好了webpack-dev-server,它是开发时的一个服务器,把打包的文件全部放入内存中,可以热更新,热替换等方便我们开发。
npm i webpack-dev-server ---save-dev

完整简单配置:

const path = require('path');
module.exports = {
    entry:path.resolve(__dirname,'src','index.js'),
    output:{
        path: path.resolve(__dirname,'dist'),
        filename:'bundle.js'
    },
    devServer:{//配置此静态文件服务器,可以用来预览打包后项目
        contentBase:path.resolve(__dirname,'dist'),//开发服务运行时的文件根目录
        host:'localhost',//主机地址
        port:9090,//端口号
        compress:true//开发服务器是否启动gzip等压缩
    }
}

4.css文件处理 css-loader用来解析处理CSS文件中的url路径,要把CSS文件变成一个模块,style-loader 可以把CSS文件变成style标签插入head中。执行顺序从右向左依次执行,先走css-loader,再走style-loader.

npm i style-loader css-loader ---save-dev
rules: [
            {
                test:/\.css$/,
                loader:['style-loader','css-loader']
            }
        ]

5.产出html

npm i html-webpack-plugin ---save-dev
plugins:[
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src','index.html'),//模板
            filename:'index.html',
            hash:true,//防止缓存
            minify:{
                removeAttributeQuotes:true//压缩 去掉引号
            }
        })
],

6.提取css文件为单独文件,不是像上面直接打包进入js中,注意extract-text-webpack-plugin 必须下载next版本 不然不支持webpack4

npm install --save-dev extract-text-webpack-plugin@next
 rules: [
            {
                test:/\.css$/,
                // loader:['style-loader','css-loader']
                use:ExtractTextWebapckPlugin.extract({
                    use:'css-loader'
                })//不再需要style-loader

            }
        ]
new ExtractTextWebapckPlugin('css/index.css')
  1. sass less 文件处理
npm i less less-loader ---save-dev
npm i node-saas sass-loader ---save-dev
const lessExtract = new ExtractTextWebapckPlugin('css/less.css');
const sassExtract = new ExtractTextWebapckPlugin('css/sass.css');
 {
    test:/\.less$/,
    use:lessExtract.extract({
        use: ['css-loader','less-loader']
    })
},
{
    test:/\.scss$/,
    use: sassExtract.extract({
        use:['css-loader','sass-loader']
    })
}
  1. babel转换js
npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 --save-dev
{
    test: /\.js/,
    use: {
        loader: 'babel-loader',
        query: {
            presets: ["env", "stage-0"]
        }
}
  1. 常用的三种加载图片的方式
  • css中引入
body{
    color: red;
    background: url(./images/002.jpg) no-repeat;
}
  • js动态引入
let imgSrc = require('./images/002.jpg');
let img = new Image();
img.src = imgSrc;
document.body.appendChild(img);
  • html img标签
  <img src="./images/002.jpg" alt="">

前两种 使用 file-loader( 解决CSS等文件中的引入图片路径问题) url-loader(当图片较小的时候会把图片BASE64编码,大于配置的limit参数的时候还是使用file-loader 进行拷贝)

npm i file-loader url-loader --save-dev
{
test:/\.(jpg|png|gif|svg)$/,
use:'url-loader',
include:path.join(__dirname,'./src'),
exclude:/node_modules/
}

第三种使用html-withimg-loader进行处理

npm i html-withimg-loader --save-dev
{
    test:/\.(html|htm)$/,
    use:'html-withimg-loader'
}
  1. 多入口问题 数组的模式并不是多入口,一下入口最终只生成一个js
entry: [path.resolve(__dirname, 'src', 'index.js'),path.resolve(__dirname, 'src', 'base.js')],

多入口的正确写法是对象的形式,以下入口会产出两个js文件

entry:{
        index:'./src/index.js',
        base:'./src/base.js'
},

多入口对应的html加载模块用chunk区分

 new HtmlWebpackPlugin({
    template: path.resolve(__dirname,'src','index.html'),
    filename:'index.html',
    chunks:['index'],
    hash:true,//防止缓存
    minify:{
        removeAttributeQuotes:true//压缩 去掉引号
    }
}),
new HtmlWebpackPlugin({
    template: path.resolve(__dirname,'src','index.html'),
    filename:'base.html',
    chunks:['base'],
    hash:true,//防止缓存
    minify:{
        removeAttributeQuotes:true//压缩 去掉引号
    }
}),
  1. 加入两个文件都需要引入一个第三方库,比如lodash,这时候我们再每个模块内部都需要引入一下,因此打包的时候每个入库都会把lodash打包进去,文件变得很大,这时候我们可以把lodash变成一个入口文件,然后通过webpack.ProvidePlugin暴露变量,在html中chunk中引用,具体如下:
entry:{
        index:'./src/index.js',
        base:'./src/base.js',
        vendor:'lodash'
} 
new webpack.ProvidePlugin({
            _:'lodash'
})
new HtmlWebpackPlugin({
    template: path.resolve(__dirname,'src','index.html'),
    filename:'base.html',
    chunks:['base','vendor'],
    hash:true,//防止缓存
    minify:{
        removeAttributeQuotes:true//压缩 去掉引号
    }
}),

我们也可以通过expose-loader向全局暴露一个对象

let _=require('expose-loader?_!lodash');
  1. watch监听文件打包变化,当文件变化时自动打包
watch: true,
watchOptions: {
    ignored: /node_modules/, //忽略不用监听变更的目录
    aggregateTimeout: 500, //防止重复保存频繁重新编译,500毫米内重复保存不打包
    poll:1000 //每秒询问的文件变更的次数
},
  1. resolve解析(比较常用,可以不写文件后缀名)
resolve:{
    extensions: ["",".js",".css",".json"]
},

14.压缩js,让输出的JS文件体积更小、加载更快、流量更省,还有混淆代码的加密功能

npm i uglifyjs-webpack-plugin --save-dev
直接new一下即可
new UglifyjsWebpackPlugin()

小结

常见的基本用法大概就总结了这么多,有兴趣可以接着阅读Webpack4优化之路