[愣锤笔记]中高级前端极速通关webpack4(图文详情持续更新中)

前端构建工具从grunt、glup+fis、rollup,到现在伴随mvvm大热的webpack也迎来了4x的版本(parcel势头也很猛,将来可期~)。熟练掌握webpack也成了中高级前端的加分项,甚至是必备项。任什么时候候,无论构建工具怎么变,其本质的构建思想是不变的,例如咱们须要在开发环境有热更新,在生成环境须要压缩文件,抽离公共库等。css

本文旨在分享快速掌握webpack4的核心内容,一块儿快速通关,争取人人都是webpack配置工程师~~html

基础安装

  • cURL是一个利用URL语法在命令行下工做的文件传输工具
// 查看curl的命令帮助
curl --help

// 能够利用curl安装nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
复制代码
  • nvm是node.js的包管理工具,地址

专门给OSX/Linux系统使用的,window可使用nvm-windows,具体查看github前端

// 将nvm命令写入path
source ~/.bash_profile
// 查看是否安装成功
nvm // 有输出内容则安装成功

// 若是写入命令时提示no such profile
// 则在终端中进行以下操做
cd ~ // 进入用户的home目录
open .bash_profile // 打开配置文件
// 没有该文件则执行以下命令建立一个该文件
touch .bash_profile
// 建立完成后,从新打开 open .bash_profile 
// 在打开的文件中键入以下内容并保存
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

// 最后查看安装是否成功
nvm
// 查看本地已安装的node
nvm list
复制代码
  • 安装webpack4
// webpack4是webpack和webpack-cli分离开的
// 须要分别安装,开发使用
cnpm i webpack webpack-cli --save-dev

// 查看有没有安装成功
// 直接找到node_modules中的webpack查看版本
./node_modules/bin/webpack -v
复制代码
  • npm运行webpack
// package.json增长npm命令
"scripts": {
    "build": "webpack"
}

// 经过npm命令进行webpack打包
npm run build
复制代码

核心概念

  • webpack一切皆模块,entry来指定webpack打包的入口;html5

  • webpack根据entry入口文件,找到对应的依赖关系,造成一个依赖图,而后根据这个依赖图打包成各类文件;node

  • output用来告诉webpack如何将编译后的文件输出到磁盘中;react

// path用来指定输出文件的地址
// filename用来指定输出文件的名称,
// [name]表示占位符,用文件的名称来命名。
// 对于多入口文件的打包,用占位符
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js'
},
复制代码
  • webpack只支持js/json类型的文件,对于其余webpack不支持的文件类型(如css/ts/image/less等),须要经过loader转换成有效的模块。webpack

  • plugin插件,用于加强webpack的功能,例如bundle文件的优化,资源管理和环境变量注入等。基本上,loader作不到的事情,plugin能够用来处理。ios

  • mode,设置webpack的模式,值有development、production、none;git

// 即对应的设置process.env.NODE_ENV的值

// process.env.NODE_ENV的值为'development'
// 开启一些和本地开发相关的优化
mode: 'development'

// process.env.NODE_ENV的值为'production'
// 开启一些和生成相关的优化
mode: 'production'

// process.env.NODE_ENV的值为'none'
// 不开启任何优化
mode: 'none'
复制代码

loader

  • 解析css
// 本地安装
// css-loader 用于将导入的css文件解析成commonJs
// style-loader 用于将css以style标签插入head中
cnpm i css-loader style-loader -D

module: {
    rules: [
        test: /.css$/,
        // 解析规则是从右到左的
        // 因此须要先解析成commonJs模块
        // 而后在以style标签形式插入到head中
        use: [
            'style-loader',
            'css-loader'
        ]
    ]
}
复制代码
  • 解析less
// 本地安装less和less-loader
cnpm i less less-loader -D

// 处理less文件
module: {
    rules: [
        test: /.less$/,
        // 先将less处理成css
        // 而后再按css的规则去处理
        use: [
            'style-loader',
            'css-loader',
            'less-loader'
        ]
    ]
}
复制代码
  • 解析图片
// 安装file-loader,用于解析图片
cnpm i file-loader -D

// 配置
module: {
    rules: [
        test: /.(jpg|jepg|png|gif)$/,
        use: 'file-loader'
    ]
}
复制代码
  • 解析字体文件
// 用的也是file-loader
module: {
    rules: [
        test: /.(woff|woff2|eot|ttf|otf|svg)$/,
        use: 'file-loader'
    ]
}

// 例如阿里云图表的引入
// 在css/less中引入图标
@font-face {
    font-family: 'iconfont';
    src: url('./font/iconfont.eot');
    src: url('./font/iconfont.eot?#iefix') format('embedded-opentype'),
        url('./font/iconfont.woff2') format('woff2'),
        url('./font/iconfont.woff') format('woff'),
        url('./font/iconfont.ttf') format('truetype'),
        url('./font/iconfont.svg#iconfont') format('svg');
}
.iconfont {
    font-family: "iconfont" !important;
    font-size: 16px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

// 页面内使用
<i class="iconfont">&#xe654;</i>
复制代码

文件监听

// 方式1,经过watch命令
// 增长npm script命令
"scripts": {
    "watch": "webpack --watch"
}

// 方式2,webpack.config.js中增长配置
module.exports = {
    watch: true,
    watchOptions: {
        // 指定忽略监控的文件,能提高一些性能
        ignored: /node_modules/,
        // 监听到变化后,等待多长时间才去执行
        aggregateTimeout: 3000,
        // 监听变化是经过轮询文件系统有么有发生变化
        // 这里设置每秒的轮询次数
        poll: 1000
    }
}
复制代码

这两种方案,都有一个弊端,就是不会自动刷新,须要咱们在浏览器手动刷新才能看到变化。
开发中,咱们更经常使用的是使用热更新。github

  • webapck-dev-server热更新(简称WDS)
// 配置命令
// --open是服务启动完成后,自动打开浏览器
"script": {
    "dev": "webpack-dev-server --open"
}

// 配置webpack.config.js
const webpakc = require('webpack');

moudle.exports = {
    // 热更新的mode须要是开发模式
    mode: 'development',
    // 须要配置下面这个插件一块儿使用
    // 在derServer开启了hot: true以后,其实不用手动引入
    // webpack会自动引入这个插件
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer: {
        contentBase: './dist',
        hot: true
    }
}
复制代码

WDS不会刷新浏览器。
WDS不输出文件,只是放在内存中,所以速度更快。
watch是输出到磁盘中。

// 热更新原理:
// 1.启动阶段:
(1)文件经过webpack-complier编译后,将生成的文件传递给bundle-server,
    bundle-server开启一个服务来支持文件经过相似localhsot:8080的方式在browser访问
(2)HMR-Server在生成的文件中注入一个HMR-Runtime运行时,
    用来和brwoser创建链接通讯,以便在文件更新时通知browser
// 2.文件更新阶段
(1)文件经过webpack-complier编译后,将更新的内容传递给HMR-Server。
(2)HMR和HMR-Runtime通讯,将更新的内容一般以json的形式传递,
    HMR-Runtime局部更新bundle.js的文件内容。
复制代码

image

补充:
(1)webpack-complier:将文件生成bundles.js
(2)HMR—Server:将热更新的文件传递给HMR-Runtime
(3)HMR-Runtime:被注入到bundle.js,用来更新文件的变化
(4)bundles.js是构建后输出的文件

  • 额外先说明区分dev和prod的配置
    (1)建立两个配置文件

image

(2)package.json中区分dev和prod的命令

image
(3)两个文件的配置要区分,一些配置只能用在dev,例如WDS;一些配置只能用于prod,例如MiniCssExtractPlugin;注意区分mode类型:

mode: 'production', // prod
mode: 'development', // dev
复制代码

文件指纹

咱们常常看到的带有各类相似hash(例如:app.38972982.js)的文件,这就能够简单理解为文件指纹。

生成文件指纹的方式有三种:

  • Hash:根据构建目录生成,只要项目中有文件修改,则整个项目构建出来的指纹都会改变。
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]_[hash].js'
},
复制代码

image

  • ChunkHash:和webpack打包到chunk有关,不一样entry会生成不一样的ChunkHush。chunk之间互不影响。
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]_[chunkhash:8].js'
},
复制代码

image

  • ContentHash:根据文件内容生成hash,只要文件内容不变,生成的ContentHash就不变。
output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name]_[contenthash].js'
},
复制代码

image

补充:
(1)冒号后面指定生成的指纹的位数。

(2)通常咱们打包的chunk文件,即js使用chunkhash模式;css使用contenthash模式;
(3)file-loader的name设置,指定打包后的指纹策略:

image

[path]能够在生成的文件中生成对应的和源码文件同样 的目录。

css打包成文件

  • 抽离css文件
// 安装本地插件
cnpm i mini-css-extract-plugin -D

// 引入
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 使用
plugins: [
    new MiniCssExtractPlugin({
      filename: '[name]_[contenthash:8].css'
    })
]

// 注意,该插件不能和style-loader使用,
// 由于style-loader是用来将css以style的方式插入的
{
    test: /.css$/,
    use: [
        // 须要将loader中css/less等文件里面的style-loader,
        // 替换成该插件
        MiniCssExtractPlugin.loader,
        'css-loader'
    ]
},
复制代码
  • 压缩css文件
// 安装压缩css插件
cnpm i optimize-css-assets-webpack-plugin -D
// 同时须要配合cssnano插件使用
cnpm i cssnano -D

// 使用
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

plugins: [
    new OptimizeCssAssetsWebpackPlugin({
        assetsNameReg: /\.css$/g,
        cssProcessor: require('cssnano')
    })
]
复制代码
  • 自动增长css兼容
// 首先安装postcss-loader和autoprefixer
cnpm i postcss-loader autoprefixer -D

// 对样式处理文件增长autoprefixer配置
// 处理css私有前缀和兼容
// 例如,增长以下配置
{
    test: /.less$/,
    use: [
        MiniCssExtractPlugin.loader,
        'css-loader',
        'less-loader',
        // 增长autoprefixer处理
        {
            loader: 'postcss-loader',
            options: {
                plugins: () => [
                    require('autoprefixer')({
                        // 经过browsers选项指定兼容的版本
                        // 推荐在webpack中配置在package.json中
                        browsers: [
                            'last 2 version',
                            '>1%',
                            'ios 7'
                        ]
                    })
                ]
            }
      }
    ]
}
复制代码

autoprefixer配置文档

  • px转换成rem
// rem的适配,引用lib-flexible,生产使用
cnpm i lib-flexible -S

// px自动转换成rem,本地安装
cnpm i px2rem-loader -D

// 配置loader,在打包时自动将px转换成rem
plugins: [
    // 处理less文件
    {
        test: /.less$/,
        use: [
            // 省略其余loader
            ……
            // 将px转换成rem
            {
                loader: 'px2rem-loader',
                options: {
                    // 对应比例,750px设计稿
                    remUnit: 75,
                    // 转换成rem时保留的小数位
                    remPrecision: 8
                }
            }
        ]
      },
]
复制代码

生成html模版

// 安装html-webpack-plugin插件
cnpm i html-webpack-plugin -D

// 使用
plugins: [
    new HtmlWebpackPlugin({
        template: path.join(__dirname, 'src/index.html'),
        filename: 'index.html',
        chunks: ['react'],
        inject: true,
        minify: {
            // html5
            html5: false,
            // 最小化css
            minifyCSS: true,
            // 最小化js
            minifyJS: true,
            // 合并空格
            collapseWhitespace: true,
            // 移除注释
            removeComments: true,
            // 是否保留换行
            preserveLineBreaks: false
        }
    })
]
复制代码

(1)template: 指定生成的html模板
(2)filename: 指定生成的文件名称
(3)chunks:指定将哪些chunk插入到html中
(4)inject:true/false,是否将chunk插入到html的body中;'body'表示插入到body中,'head'表示插入到head中; (5)minify:设置压缩参数。
html-webpack-plugin官网

自动清理构建产物

  • 手动清理很麻烦
// 终端输入
rm -rf dist
复制代码
  • 利用webpack在每次打包时自动清理以前的产物
// 安装clean-webpack-plugin
cnpm i clean-webpack-plugin -D

// 使用
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 之前版本引入时,不须要花括号
const CleanWebpackPlugin = require('clean-webpack-plugin');

// 使用
plugins: [
    new CleanWebpackPlugin()
]
复制代码

文档地址

静态资源内联

// 本地安装raw-loader,这里装的是0.5.0的版本
cnpm i raw-loader@0.5.0 -D

// 在html文件中
// 例如将公共的meta资源内联
<head>
    <!-- 只须要在对应的位置插入内联代码 -->
    ${ require('raw-loader!../meta.html') }
</head>

// meta.html示例
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">

// 内联js,例如lib-flexible.js
// 咱们须要将它内联在head中加载来保证更快
<head>
    <script>
        // 咱们这里先对其进行babel转译后再内联
        ${ require('raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js') }
    </script>
</head>
复制代码

内联后生成的html效果以下:

image

更多实用黑科技未完待续,持续更新中~~~

百尺竿头、日进一步。 我是愣锤,欢迎交流与分享。 若是以为喜欢,记得点赞收藏哦~~

相关文章
相关标签/搜索