目录文章目录概念最简单的打包器配置文件多种配置类型入口输出hash、chunkhash、contenthash模式自动生成页面loadercss-loader 和 style-loadersass-loader 和 less-loaderpostcss-loaderbabel-loaderfile-loader 和 url-loaderhtml-withimg-loader搭建开发环境pluginsclean-webpack-pluginmini-css-extract-pluginoptimize-css-assets-webpack-plugincopy-webpack-pluginProvidePluginloader 和 plugin 的区别结语 周末本来是要出去浪的,结果由于疫情的原因,再加上天又这么热,想想还是算了,还是在家里吹空调来的爽,正好项目刚刚结束,趁热打铁,把项目中用到的技术点是时候系统的整理一下,也算是对近期自己的一个自我评价,今天呢就给大家整理一下有关 Webpack 配置的全解析,属于基础篇,主要介绍 webpack 的入口、输出和各种 loader、plugins 的使用以及开发环境的搭建。大佬可以忽略,希望本篇内容对需要的小伙伴有帮助。 概念 官网对 webpack 的定义: 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。 首先 webpack 是一个静态模块打包器,所谓的静态模块,包括脚本、样式表和图片等等;webpack 打包时首先遍历所有的静态资源,根据资源的引用,构建出一个依赖关系图,然后再将模块划分,打包出一个或多个 bundle。再次白 piao 一下官网的图,生动的描述了这个过程: vue-loader 最后说一下一个比较特殊的 vue-loader,看名字就知道是用来处理 vue 文件的。 npm i -D vue-loader vue-template-compiler npm i -S vue 我们首先来创建一个 vue 文件,具体代码在 demo5 中: //src/App.vue <template> <div id="app"> <div @click="tap">{{title}}</div> </div> </template> <script> export default { name: 'app', data(){ return { title: 'app 实例' } }, methods: { tap(){ this.title = this.title.split('').reverse().join('') } } } </script> <style> #app{ font-size: 16px; background: #ccc; } </style> 然后在 webpack 的入口文件中引用它: //src/main.js import Vue from 'vue' import App from './App.vue' new Vue({ render: h => h(App) }).$mount('#app') 不过 vue-loader 和其他 loader 不太一样,除了将它和.vue文件绑定之外,还需要引入它的一个插件: const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { module: { rules: [ //省略其他 loader { test: /.vue$/, loader: 'vue-loader' }] }, plugins: [ new VueLoaderPlugin(), ] } 这样我们就能愉快的在代码中写 vue 了。 搭建开发环境 在上面的 demo 中我们都是通过命令行打包生成 dist 文件,然后直接打开 html 或者通过 static-server 来查看页面的;但是开发中我们写完代码每次都来打包会严重影响开发的效率,我们期望的是写完代码后立即就能够看到页面的效果;webpack-dev-server 就很好的提供了一个简单的 web 服务器,能够实时重新加载。 首先在我们的项目中安装依赖: npm i -D webpack webpack-dev-server webpack-dev-server 的用法和 wepack 一样,只不过他会额外启动一个 express 的服务器。我们在项目中新建一个webpack.dev.config.js配置文件,单独对开发环境进行一个配置,相关代码在 demo6 中: module.exports = { //省略其他配置 devServer: { //启动服务器端口 port: 9000, //默认是 localhost,只能本地访问 host: "0.0.0.0", //自动打开浏览器 open: false, //启用模块热替换 hot: true, //启用 gzip 压缩 compress: true }, plugins: [ //热更新插件 new webpack.HotModuleReplacementPlugin({ }) ] } 通过命令行webpack-dev-server来启动服务器,启动后我们发现根目录并没有生成任何文件,因为 webpack 打包到了内存中,不生成文件的原因在于访问内存中的代码比访问文件中的代码更快。 我们在public/index.html的页面上有时候会引用一些本地的静态文件,直接打开页面的会发现这些静态文件的引用失效了,我们可以修改 server 的工作目录,同时指定多个静态资源的目录: contentBase: [ path.join(__dirname, "public"), path.join(__dirname, "assets") ] 热更新(Hot Module Replacemen 简称 HMR)是在对代码进行修改并保存之后,webpack 对代码重新打包,并且将新的模块发送到浏览器端,浏览器通过新的模块替换老的模块,这样就能在不刷新浏览器的前提下实现页面的更新。 plugins 在上面我们也介绍了 DefinePlugin、HtmlWebpackPlugin 等很多插件,我们发现这些插件都能够不同程度的影响着 webpack 的构建过程,下面还有一些常用的插件,plugins 相关代码在 demo7 中。 clean-webpack-plugin clean-webpack-plugin 用于在打包前清理上一次项目生成的 bundle 文件,它会根据 output.path 自动清理文件夹;这个插件在生产环境用的频率非常高,因为生产环境经常会通过 hash 生成很多 bundle 文件,如果不进行清理的话每次都会生成新的,导致文件夹非常庞大;这个插件安装使用非常方便: npm i -D clean-webpack-plugin 安装后我们在 config 中配置一下就可以了: const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { //其他配置 plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: './public/index.html', filename: 'index.html', }) ] } mini-css-extract-plugin 我们之前的样式都是通过 style-loader 插入到页面中去,但是生产环境需要单独抽离样式文件,mini-css-extract-plugin 就可以帮我从 js 中剥离样式: npm i -D mini-css-extract-plugin 我们在开发环境使用 style-loader,生产环境使用 mini-css-extract-plugin: const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { //其他配置 module: { rules: [ { test: /.less/, use: [{ loader: isDev ? 'style-loader' : MiniCssExtractPlugin.loader },{ loader: 'css-loader' },{ loader: 'less-loader' }] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].[hash:8].css", }) ] } 引入 loader 后,我们还需要配置 plugin,提取的 css 同样支持output.filename中的占位符字符串。 optimize-css-assets-webpack-plugin 我们可以发现虽然配置了production模式,打包出来的 js 压缩了,但是打包出来的 css 确没有压缩;在生产环境我们需要对 css 进行一下压缩: npm i optimize-css-assets-webpack-plugin -D 然后也是引入插件: const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); module.exports = { //其他配置 plugins: [ new OptimizeCSSAssetsPlugin(), ] } copy-webpack-plugin 和 demo6 中一样,我们在 public/index.html 中引入了静态资源,但是打包的时候 webpack 并不会帮我们拷贝到 dist 目录,因此 copy-webpack-plugin 就可以很好地帮我做拷贝的工作了 npm i -D copy-webpack-plugin 在 config 中配置我们需要拷贝的源路径和目标路径: const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { plugins: [ new CopyWebpackPlugin({ patterns: [ { from: 'public/js/*.js', to: path.resolve(__dirname, 'dist', 'js'), flatten: true, } ] }), ] } ProvidePlugin ProvidePlugin 可以很快的帮我们加载想要引入的模块,而不用 require。一般我们加载 jQuery 需要先把它 import: import $ from 'jquery' $('.box').html('box') 但是我们在 config 中配置 ProvidePlugin 插件后能够不用 import,直接使用$: module.exports = { plugins: [ new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), ] } 但是如果在项目中引入了太多模块并且没有 require 会让人摸不着头脑,因此建议加载一些常见的比如 jQuery、vue、lodash 等。 loader 和 plugin 的区别 介绍了这么多 loader 和 plugin,我们来回顾一下他们两者的区别: loader:由于 webpack 只能识别 js,loader 相当于翻译官的角色,帮助 webpack 对其他类型的资源进行转译的预处理工作。 plugins:plugins 扩展了 webpack 的功能,在 webpack 运行时会广播很多事件,plugin 可以监听这些事件,然后通过 webpack 提供的 API 来改变输出结果。 结语 Webpack 凭借强大的功能,成为最流行和最活跃的打包工具,也是面试时高级程序员必须掌握的“软技能”,所以有必要好好的去系统的学习一下。文章有点长,建议先收藏 CTRL + D 后看,本文是 webpack 基础篇,还有很多生产环境的优化还没有写到;因此各位看官敬请期待优化篇。 本文所有的 demo 代码均在WebpackDemo 推荐阅读: 《webpack 系列一之如何写一个自己的 loader》 《webpack 源码解析二之探究 style-loader 和 css-loader 是如何工作的》 《webpack 源码解析三之 file-loader 和 url-loader》 《webpack 源码解析四之 bundle.js 内容分析》 《webpack 源码解析五之 webpack 处理流程分析》 《webpack 源码解析六之 HMR 热更新原理》 文章参考:公众号【前端壹读】,感兴趣的可以关注一下博主