webpack4

1-安装webpack

准备工作:

  1. 需要先在项目中npm init初始化一下,生成package.json
  2. 建议node版本安装到8.2以上
// webpack4中除了正常安装webpack之外,需要再单独安一个webpack-cli
npm i webpack webpack-cli -D

npm i -D 是 npm install --save-dev 的简写,是指安装模块并保存到 package.json 的 devDependencies中,主要在开发环境中的依赖包

2- 0配置是什么?

webpack4可以支持0配置打包,这里所说的0配置又是什么呢?当然在开发者眼中0配置的东西,那根本是无法用的,因为不够智能,那么我们就来看看做到了哪些0配置
在使用webpack进行打包的时候,默认情况下会将src下的入口文件(index.js)进行打包

// node v8.2版本以后都会有一个npx
// npx会执行bin里的文件
npx webpack     // 不设置mode的情况下 打包出来的文件自动压缩
npx webpack --mode development  // 设置mode为开发模式,打包后的文件不被压缩

当执行npx webpack命令的时候,webpack会自动查找项目中src目录下的index.js文件,然后进行打包,生成一个dist目录并存在一个打包好的main.js文件
这些算是0配置的操作了,名字都是定义好的,不能变,想想也很鸡肋

npx webpack --mode development

打包后的文件目录

webpack的使用还是在我们的配置方面,下面就进入我们的常规操作环节

3- webpack是基于Node的

在项目下创建一个webpack.config.js(默认,可修改)文件来配置webpack

module.exports = {
    entry: '',               // 入口文件
    output: {},              // 出口文件
    module: {},              // 处理对应模块
    plugins: [],             // 对应的插件
    devServer: {},           // 开发服务器配置
    mode: 'development'      // 模式配置
}

以上就是webpack的正常配置模块
★ 启动devServer需要安装一下webpack-dev-server

npm i webpack-dev-server -D

按照项目的结构,我们就从0开始去写一下配置吧

// webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/index.js', // 入口文件
    output: {
        filename: 'bundle.js', // 打包后的文件名称
        path: path.resolve('dist') 
         // 打包后的目录,必须是绝对路径, path将相对路径处理成绝对路径
    }, // 出口文件
    module: {}, // 处理对应模块
    plugins: [], // 对应的插件
    devServer: {}, // 开发服务器配置
    mode:'development', // 模式配置
}

上面就可以说是实现了最简单的webpack配置了,那接下来就打包一下看看

npx webpack后的项目目录

4- 配置执行文件

工作当中我们打包编译的时候一般都执行npm run dev这样的命令,既然是通过npm执行的命令,我们就应该找到package.json里的执行脚本去配置一下命令,这里如下图所示


配置执行文件

npm run build就是我们打包后的文件,这是生产环境下,上线需要的文件
npm run dev是我们开发环境下打包的文件,当然由于devServer帮我们把文件放到内存中了,所以并不会输出打包后的dist文件夹
通过npm run build之后会生成一个dist目录文件夹,就和上面打包后的样子一样了

5- 多入口文件

多个入口可以有两种实现方式进行打包

  1. 一种是没有关系的但是要打包到一起去的,可以写一个数组,实现多个文件打包
  2. 另一种就是每一个文件都单独打包成一个文件的
    下面就来看看这两种方式的写法
let path = require('path');

module.exports = {
    // 1.写成数组的方式就可以打出多入口文件,不过这里打包后的文件都合成了一个
    // entry: ['./src/index.js', './src/login.js'],
    // 2.真正实现多入口和多出口需要写成对象的方式
    entry: {
        index: './src/index.js',
        login: './src/login.js'
    },
    output: {
        // 1. filename: 'bundle.js',
        // 2. [name]就可以将出口文件名和入口文件名一一对应
        filename: '[name].js',      // 打包后会生成index.js和login.js文件
        path: path.resolve('dist')
    }
}
多入口多出口, 这时候执行npm run build后,会生成打包好的两个js文件,如图所示

6- 配置Html模板

文件都打包好了,但是我们在使用的时候不能在dist目录下去创建一个html文件,然后去引用打包后的js,这不合理,实际开发中也不会这样
我们需要实现html打包功能,可以通过一个模板实现打包出引用好路径的html来(这个html文件中已经引用了打包好的js文件)这就需要用到一个常用的插件了,html-webpack-plugin,用之前我们来安一下它

npm i html-webpack-plugin -D

因为是个插件,所以需要在config.js里引用一下, 并进行配置的

6.1- 单页面开发配置

let path = require('path');
// 插件都是一个类,所以我们命名的时候尽量用大写开头
let HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        // 添加hash可以防止文件缓存,每次都会生成4位的hash串
        filename: 'bundle.[hash:4].js',   
        path: path.resolve('dist')
    },
    plugins: [
        // 通过new一下这个类来使用插件
        new HtmlWebpackPlugin({
            // 用哪个html作为模板
            // 在src目录下创建一个index.html页面当做模板来用
            template: './src/index.html',
            hash: true, // 会在打包好的bundle.js后面加上hash串
        })
    ]
}

通过上面的配置后,我们再npm run build打包看一下现在是个什么样子了


使用html-webpack-plugin插件打包后的文件目录

6.2- 多页面开发配置

如果开发的时候不只一个页面,我们需要配置多页面,那么需要怎么来搞呢?不用担心,html-webpack-plugin插件自有办法,我们来观望一下

const path = require('path');
// 插件都是一个类,所以我们命名的时候尽量用大写开头
let HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // entry: ['./src/index.js', './src/login.js'],
    // 真正实现多入口和多出口需要写成对象的方式
    entry: {
        index: './src/index.js',
        login: './src/login.js',
    },
    output: {
        filename: '[name].js',
        path: path.resolve('dist'), 
    },
    mode:'development', // 模式配置
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            chunks: ['index'] // 对应关系,index.js对应的是index.html
        }),
        new HtmlWebpackPlugin({
            template: './src/login.html',
            filename: 'login.html',
            chunks: ['login'], // 对应关系,login.js对应的是login.html
        })
    ]
}

继续npm run build看打包后的样子



上面基本介绍完了html和js的打包配置了,现在我们还缺一个好兄弟css,webpack对css的解析需要用到loader,所以我们先提前安装好,待会好方便使用

7. 引用CSS文件

可以在src/index.js里引入css文件,到时候直接打包到生产目录下
需要下载一些解析css样式的loader

npm i style-loader css-loader -D
// 引入less文件的话,也需要安装对应的loader
npm i less less-loader -D

在webpack.config.js中添加相关配置

    module: {
        rules: [
            { 
                test: /\.css$/,  // 解析css
                use: ['style-loader', 'css-loader'], // 从右向左解析
            },
            {
                test: /\.less$/, // 解析less
                use: [
                    {loader: 'style-loader'},  //  也可以这样写成对象,这种方式方便写一些配置参数
                    {loader: 'css-loader'},
                    {loader: 'less-loader'},
                ]
            }
        ]
    }

浏览器打开打包后的index.html


浏览器
  • 此时打包后的css文件是以行内样式style的标签写进打包后的html页面中,如果样式很多的话,我们更希望直接用link的方式引入进去,这时候需要把css拆分出来
  • 解决方案:extract-text-webpack-plugin插件,它的功效就在于会将打包到js里的css文件进行一个拆分

7.1- 拆分打包后的css

// @next表示可以支持webpack4版本的插件
npm i extract-text-webpack-plugin@next -D

webpack.config.js新增配置

// 拆分css样式的插件
let ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
module.exports = {
  entry: '',
  output: {},
  module: {
     rules: [
       {
        test: /\.css$/,
        use: ExtractTextWebpackPlugin.extract(
          {use: "css-loader"}  // 将css用link的方式引入就不再需要style-loader了
        )
    }
  ]
  },
plugins:[
 // 拆分后会把css文件放到dist目录下的css/style.css
    new ExtractTextWebpackPlugin('css/style.css)
]
}

此时拆分完css后,打包的html页面就以link的方式去引入css了,这样很好


link引用打包后的css

当然大家很多都说另外一个插件也是可以办到的,那就是mini-css-extract-plugin,是的可以说它是为webpack4而生的,而之所以上来就没有介绍是因为还不成熟,还有很多bug需要去解决的

不过既然大家都知道它,那就顺便也提一下吧

npm i mini-css-extract-plugin -D

使用起来和上面的插件是差不多的

let MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/a.css'   // 指定打包后的css
        })
    ]
}

7.2- 拆成多个css

这里要着重说一下上面两个插件的区别了,我个人还是建议用extract-text-webpack-plugin的,毕竟从之前的版本承接下来的,虽然在安包的时候需要@next,但是还是值得信赖的

而且现在的extract-text-webpack-plugin也支持了拆分成多个css,而目前mini-css-extract-plugin还不支持此功能

// 正常写入的less
let styleLess = new ExtractTextWebpackPlugin('css/style.css');
// reset
let resetCss = new ExtractTextWebpackPlugin('css/reset.css');

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: resetCss.extract({
                    use: 'css-loader'
                })
            },
            {
                test: /\.less$/,
                use: styleLess.extract({
                    use: 'css-loader'
                })
            }
        ]
    },
    plugins: [
        styleLess,
        resetCss
    ]
}

通过这样操作后可以打包成两个不同的css文件,如下图


目录

8-引用图片

  • 处理图片方面,也需要loader
npm i file-loader url-loader -D

如果是在css文件里引入的如背景图之类的图片,就需要指定一下相对路径

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextWebpackPlugin.extract({
                    use: 'css-loader',
                    publicPath: '../'
                })
            },
            {
                test: /\.(jpe?g|png|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192,   
                            // 小于8k的图片自动转成base64格式,并且不会存在实体图片
                            outputPath: 'images/'   // 图片打包后存放的目录
                        }
                    }
                ]
            }
        ]
    }
}

在css中指定了publicPath路径这样就可以根据相对路径引用到图片资源了,如下图所示


8.1页面img引用图片

页面中经常会用到img标签,img引用的图片地址也需要一个loader来帮我们处理好

npm i html-withimg-loader -D

这样再打包后的html文件下img就可以正常引用图片路径了


8.2-引用字体图片和svg图片

字体图标和svg图片都可以通过file-loader来解析

module.exports = {
    module: {
        rules: [
            {
                test: /\.(eot|ttf|woff|svg)$/,
                use: 'file-loader'
            }
        ]
    }
}

这样即使样式中引入了这类格式的图标或者图片都没有问题了,img如果也引用svg格式的话,配合上面写好的html-withimg-loader就都没有问题了

9-添加CSS3前缀

通过postcss中的autoprefixer可以实现将CSS3中的一些需要兼容写法的属性添加响应的前缀,这样省去我们不少的时间
由于也是一个loader加载器,我们也需要先安装一下

npm i postcss-loader autoprefixer -D

安装后,我们还需要像webpack一样写一个config的配置文件,在项目根目录下创建一个postcss.config.js文件,配置如下:

module.exports = {
    plugins: [require('autoprefixer')]  // 引用该插件即可了
}

然后在webpack里配置postcss-loader

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

10-转义ES6

在实际开发中,我们在大量的使用着ES6及之后的api去写代码,这样会提高我们写代码的速度,不过由于低版本浏览器的存在,不得不需要转换成兼容的代码,于是就有了常用的Babel了
Babel会将ES6的代码转成ES5的代码
既然要使用它,就先来安一下

npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 -D

当把这些都安好后,我们就开始配置,由于要兼容的代码不仅仅包含ES6还有之后的版本和那些仅仅是草案的内容,所以我们可以通过一个.babelrc文件来配置一下,对这些版本的支持

// .babelrc
{
    "presets": ["env", "stage-0"]   // 从右向左解析
}

我们再在webpack里配置一下babel-loader既可以做到代码转成ES5了

module.exports = {
    module: {
        rules: [
            {
                test:/\.js$/,
                use: 'babel-loader',
                include: /src/,          // 只转化src目录下的js
                exclude: /node_modules/  // 排除掉node_modules,优化打包速度
            }
        ]
    }
}

在我们每次npm run build的时候都会在dist目录下创建很多打好的包,如果积累过多可能也会混乱

所以应该在每次打包之前将dist目录下的文件都清空,然后再把打好包的文件放进去

这里提供一个clean-webpack-plugin插件

npm i clean-webpack-plugin -D
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = {
    plugins: [
        // 打包前先清空
        new CleanWebpackPlugin()  
    ]
}

11-启动静态服务器

启动一个静态服务器,默认会自动刷新,就是说你对html,css,js文件做了修改并保存后,浏览器会默认刷新一次展现修改后的效果

正常情况下我们都是在开发环境中开发项目,所以之前配置的脚本"dev"可以派上用场了,在执行npm run dev命令后,会启动静态服务器,我们访问localhost:3000端口就可以看到开发的页面内容了

如果devServer里open设为true后,会自动打开浏览器

module.exports = {
    devServer: {
        contentBase: './dist',
        host: 'localhost',      // 默认是localhost
        port: 3000,             // 端口
        open: true,             // 自动打开浏览器
        hot: true               // 开启热更新
    }
}

当然在npm run dev命令下,打包的文件存在于内存中,并不会产生在dist目录下

11.1-热更新和自动刷新的区别

在配置devServer的时候,如果hot为true,就代表开启了热更新

But这并没那么简单,因为热更新还需要配置一个webpack自带的插件并且还要在主要js文件里检查是否有module.hot

下面就让我们直接看下代码是如何实现的

// webpack.config.js
let webpack = require('webpack');

module.exports = {
    plugins: [
        // 热更新,热更新不是刷新
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer: {
        contentBase: './dist',
        hot: true,
        port: 3000
    }
}

// 此时还没完虽然配置了插件和开启了热更新,但实际上并不会生效

// index.js
let a = 'hello world';
document.body.innerHTML = a;
console.log('这是webpack打包的入口文件');

// 还需要在主要的js文件里写入下面这段代码
if (module.hot) {
    // 实现热更新
    module.hot.accept();
}

以上index.js中的内容,如果将变量a的值进行修改保存后,会在不刷新页面的情况下直接修改掉,这样就实现了热更新
那么热更新从现在看来和自动刷新浏览器的区别也不是太大嘛!自动刷新也是可以接受的啊
其实不然,热更新的好处可能在vue或者react中有更大的发挥,其中某一个组件被修改的时候就会针对这个组件进行热更新了,这里用到vue或react的同学去实际体验一下吧

12- resolve解析

在webpack的配置中,resolve我们常用来配置别名和省略后缀名

module.exports = {
    resolve: {
        // 别名
        alias: {
            $: './src/jquery.js'
        },
        // 省略后缀
        extensions: ['.js', '.json', '.css']
    },
}

13- 提取公共代码

在webpack4之前,提取公共代码都是通过一个叫CommonsChunkPlugin的插件来办到的。到了4以后,内置了一个一模一样的功能,而且起了一个好听的名字叫“优化”

下面我们就来看看如何提取公共代码

// 假设a.js和b.js都同时引入了jquery.js和一个写好的utils.js
// a.js和b.js
import $ from 'jquery';
import {sum} from 'utils';

那么他们两个js中其中公共部分的代码就是jquery和utils里的代码了

可以针对第三方插件和写好的公共文件

module.exports = {
    entry: {
        a: './src/a.js',
        b: './src/b.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve('dust')
    },
    // 提取公共代码
+   optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {   // 抽离第三方插件
                    test: /node_modules/,   // 指定是node_modules下的第三方包
                    chunks: 'initial',
                    name: 'vendor',  // 打包后的文件名,任意命名    
                    // 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
                    priority: 10    
                },
                utils: { // 抽离自己写的公共代码,utils这个名字可以随意起
                    chunks: 'initial',
                    name: 'utils',  // 任意命名
                    minSize: 0    // 只要超出0字节就生成一个新包
                }
            }
        }
+   },
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'a.html',
            template: './src/index.html',  // 以index.html为模板
+           chunks: ['vendor', 'a']
        }),
        new HtmlWebpackPlugin({
            filename: 'b.html',
            template: './src/index.html',  // 以index.html为模板
+           chunks: ['vendor', 'b']
        })
    ]
}

通过以上配置,可以把引入到a.js和b.js中的这部分公共代码提取出来,如下图所示

13-指定webpack配置文件

在package.json的脚步里,我们可以配置调用不同的webpack配置文件进行打包,举个栗子


这样的好处在于可以针对不同的需求进行一个特定的打包配置

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容