# webpack-study **Repository Path**: qinjunbang/webpack-study ## Basic Information - **Project Name**: webpack-study - **Description**: webpack学习笔记 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2019-05-27 - **Last Updated**: 2024-02-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # webpack学习笔记 #### 全局安装webpack ``` npm install webpack webpack-cli -g ``` 全局安装webpack会有个问题,就是当你有两个项目依赖于不同版本的webpack,就会有一个项目打包不了,所以还是不全局安装webpack比较好。 #### 在当前项目安装webpack 1. 新建webpack-demo目录,然后进行npm项目初始化 ``` npm init 或者 npm init -y ``` 2. 在刚创建出来的package.json中添加`private`字段 ``` "private": true /**表示私有的**/ ``` 3. 如果webpack已经全局安装,需要卸载 ``` npm uninstall webpack webpack-cli -g /**卸载全局安装的webpack**/ ``` 4. 在项目根目录下执行安装命令 ``` npm install webpack webpack-cli --save-dev ``` 5. 使用npx打印出当前的webpack版本 ``` npx webpack -v ``` #### 安装指定版本的webpack ``` npm info webpack /*查看webpack所有版本信息*/ npm install webpace@4.29.6 --save-dev ``` #### webpack的配置文件 在项目根目录新建文件webpack.config.js ``` const path = require('path'); module.exports = { entry: './index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } } ``` #### webpack手动打包命令 ``` npx webpack /**默认会使用根目录下的webpack.config.js配置文件进行打包**/ npx webpack --config webpack.config.js /**指定配置文件打包**/ ``` #### 使用npm scripts 简化webpack命令 1. 在package.json中的scripts字段下添加`bundle`字段 ``` "bundle": "webpack" ``` 2. 使用npm命令打包 ``` npm run bundle ``` #### 使用file-loader打包图片 1. 安装file-loader ``` npm install file-loader --save-dev ``` 2. 在配置文件webpack.config.js中添加字段 ``` module: { rules: [ { test: /\.(jpg|png|svg|gif)$/, use: { loader: 'file-loader', options: { name: '[name].[ext]', /**原名字输出**/ outputPath: 'images/', /**打包后存放图片的文件夹**/ } } } ] } ``` #### 使用url-loader打包图片 1. 安装url-loader ``` npm install url-loader --save-dev ``` 2. 在配置文件webpack.config.js中添加字段 ``` module: { rules: [ { test: /\.(jpg|png|svg|gif)$/, use: { loader: 'url-loader', options: { name: '[name].[ext]', outputPath: 'images/', limit: 204800, /**小于20kb的图片,打包成base64放到bundle.js文件**/ } } } ] } ``` #### 使用style-loader和css-loader打包css文件 1. 安装style-loader 和 css-loader ``` npm install style-loader css-loader --save-dev ``` 2. 在webpack.config.js文件中添加配置 ``` module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } ``` #### 使用sass-loader打包.sass文件 1. 安装sass-loader 和 node-sass ``` npm install sass-loader node-sass --save-dev ``` 2. 在webpack.config.js文件中添加配置 ``` module: { rules: [{ test: /\.sass$/, use: ['style-loader', 'css-loader', 'sass-loader'] }] } ``` > 数组形式的loader是从下到上,从右到左执行 sass-loader -> css-loader -> style-loader #### 使用postcss-loader自动添加css厂商前缀 1. 安装postcss-loader ``` npm install postcss-loader --save-dev ``` 2. postcss-loader需要配合autoprefixer插件使用 ``` npm install autoprefixer --save-dev ``` 3. 在项目根目录添加postcss.config.js文件 ``` module.exports = { plugins: [ require('autoprefixer'); ] } ``` 4. 在webpack.config.js中添加配置 ``` module: { rules: [{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader'] }] } ``` 5. 确保所有的scss文件都会被所有loader处理,修改webpack.config.js中的配置 ``` { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { /**确保每个scss都被所有loader处理**/ importLoaders: 2, /**分模块打包css**/ modules: true } }, 'sass-loader', 'postcss-loader' ] } ``` #### 使用file-loader打包字体文件 1. 安装file-loader ``` npm install file-loader --save-dev ``` 2. 在webpack.config.js文件中添加modules ``` { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } } ``` #### 使用插件使webpack打包更便捷 **1). 安装自动生成index.html插件 html-webpack-plugin** 1. 安装html-webpack-plugin ``` npm install html-webpack-plugin --save-dev ``` htmlWebpackPlugin 会在打包结束后,自动生成index.html文件,并把对应的js引入index.htm文件中 2. 在webpack.config.js中引入插件 ``` const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' /**自动以src/index.html为模板,在dist目录下生成新的index.html文件**/ }) ] ``` **2)使用clean-webpacl-plugin插件自动删除dist插件再打包** 1. 安装插件clean-webpack-plugin ``` npm install clean-webpack-plugin --save-dev ``` 2. 在webpack.config.js中使用插件 ``` const CleanWebpackPlugin = require('clean-webpack-plugin'); plugins: [new CleanWebpackPlugin({})] /**默认清除的是dist目录**/ ``` #### sourceMap配置 sourceMap 映射src目录的源文件,能定位到哪行报错 1. 开启sourceMap,在wepback.config.js中添加配置 ``` devtool: 'source-map' ``` 2. sourceMap的最佳实现 ``` devtool: 'cheap-module-eval-source-map' /**开发环境中使用**/ devtool: 'cheap-module-source-map' /**生产环境**/ ``` #### 使用WebpackDevServer提高开发效率 1. 安装webpack-dev-server ``` npm install webpack-dev-server --save-dev ``` 2. 在webpack.config.js中配置devServer ``` devServer: { contentBase: './dist', open: true, /**open true 可以自动打开浏览器**/ proxy: { "/api": "http://new.junbang.com/" /**请求api代理转发**/ }, port: 8081, /**端口号**/ } ``` 3. 在package.json中添加watch命令 ``` "scripts": { "watch": "webpack --watch", "start": "webpack-dev-server" } ``` 使用:npm run watch 监听文件有变化自动打包 使用:npm run start 可以自动监听,自动打包, 自动刷新浏览器 #### 自定义server 1. 需要安装express和webpack-dev-middleward这两个插件 ``` npm install express webpack-dev-middleware --save-dev ``` 2. 新建server.js 并引入插件 ``` const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const config = require('./webpack.config.js'); const complier = webpack(config); const app = express(); app.use(webpackDevMiddleware(complier, { publicPath: config.output.publicPath })); app.listen(3000,() => { console.log('server is running on prot 3000'); }); ``` 3. 在package.json中设置serve命令 ``` "scripts": { "serve": "node server.js" } ``` npm run serve 使用的就是我们server.js配置的服务器 不过server.js还需要写很多,这只是简单的server #### 模块热更新HMR 1. 开启模块热更新,在webpack.config.js中添加配置 ``` devServer: { hot: true, hotOnly: true, /**hotOnly: false 浏览器可以自动刷新**/ } ``` 2. 使用HMR插件,在webpack.config.js中引入插件 ``` const webpack = require('webpack'); plugins: { new webpack.HotModuleReplacementPlugin() } ``` 使用`npm run start`重启服务使新的配置生效 **模块热更新HMR作用:** > css编写. 修改无需重新刷新浏览器就可显示效果 > js模块发生改变可以指定更新当前js模块,不需要刷新浏览器 3. js模块热更新,在index.js文件中编写代码 ``` import number from './number'; number(); if (module.hot) { module.hot.accept('./number', () => { /**当number模块有改变重新渲染**/ number(); } } ``` #### 使用Babel处理ES6语法 1. 安装babel-loader 和 @babel/core ``` npm install --save-dev babel-loader @babel/core ``` 2. 在webpack.config.js中添加配置 ``` module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader", options: { presets: ["@babel/preset-env"] } }] } ``` 3. 安装@babel/preset-env模块对ES6语法进行翻译 ``` npm install @babel/preset-env --save-dev ``` 4. 在webpack.config.js中的modules rules babel-loader 中配置options对象 ``` options: { presets: ["@babel/preset-env"] } /**或者在项目根目录下创建配置文件.bablerc**/ { "presets": ["@babel/preset-env"] } ``` 5. 使用@babel/polyfill 加上低版本没有的语法, 比如map() . Promise等 ``` npm install --save @babel/polyfill ``` 6. 在所以代码运行之前,可以放在入口文件index.js最顶部 ``` import "@babel/polyfill"; ``` 7. polyfill默认会把所有翻译过代码都加进来,有时候我们没有用到的新方法,也有了翻译的方法在里面了,所以我们要过滤掉,没用上的就不要加载进来了,这样包更小,所以在webpack.config.js babel-loader中修改配置 ``` options: [['@babel/preset-env', { useBuiltIns: 'usage' }]] ``` 8. 已经支持ES6语法的浏览器版本,没必要在翻译所以我们可以指定浏览器版本 ``` options: [['@babel/preset-env', { targets: { chrome: "67" /**更多浏览器版本配置去babel官网查看**/ }, useBuiltIns: 'usage' }]] ``` 9. 当指定了useBuiltIns: 'usage',会自动引入@babel/polyfill,所以可以去掉index.js import的@babel/polyfill,但是可以需要安装依赖 ``` npm install --save core-js ``` 10. 如果是写业务代码以上配置没问题,如果要写框架. 类库. 第三方模块什么的,为了避免变量的全局污染(因为polyfille翻译的变量挂载到全局变量)而使用plugin-transform-runtime插件 11. 安装transform-runtime插件 ``` npm install --save-dev @babel/plugin-transform-runtime npm install --save #babel/runtime ``` 12. 在webpack.config.js中修改配置 ``` module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { plugins: [["@babel/plugin-transform-runtime", { corejs: 2 helpers: true, regenerator: true useESModules: false }]] } }] } ``` 13. 因为我们配置了corejs: 2,所以要加装corejs的依赖 ``` npm install --save @babel/runtime-corejs2 ``` #### 配置React代码的打包 babel也可以打包react代码 1. 安装react框架 ``` npm install react react-dom --save ``` 2. 安装@babel/preset-react ``` npm install --save-dev @babel/preset-react ``` 3. 在.babelrc中添加配置 ``` presets: ["@babel/preset-env","@babel/preset-react"] ``` #### 使用Tree Shaking去掉没有引用的方法/ 模块 Tree Shaking只支持 ES Module 就是import export不支持require('') 1. 在mode: 'development'中配置Tree Shaking, 在webpack.config.js中 ``` optimization: { usedExports: true } ``` 2. 在mode: 'production'中 默认就有,不需要添加任何配置 3. 在package.json中配置不需要Tree Shaking的模块 ``` "sideEffects": false /**所有模块都需要Tree Shaking**/ "sideEffects": ['@babel/polly-fill', '*.css'] /**对这两个模块不做Tree Shaking**/ ``` #### Development 和 Production 模式的区分打包 1. 创建webpack.common.js 放公有的配置项 2. 创建webpack.dev.js 放开发独有的配置项 3. 创建webpack.prod.js 放生产独有的配置项 4. 使用webpack-merge 把webpack.common.js合并到其他两个文件 ``` npm install webpack-merge --save-dev ``` 5. 在webpack.dev.js中引入插件 ``` const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const devConfig = { ... } module.exports = merge(commonConfig, devConfig); ``` 在webpack.prod.js中也作跟webpack.dev.js同样的处理 #### Code Splitting 代码分割 1. 同步加载模块 在webpack.config.js中配置splitChunks ``` optimization: { splitChunks: { chunks: 'all' } } ``` 2. 异步加载模块无需任何配置默认就会自动分割 3. 创建一个异步加载的js模块 ``` /** webpackChunkName: "lodash" 给这个模块起这个文件名 **/ function getComponent() { return import(/* webpackChunkName: "lodash"*/ 'lodash').then(({default: _}) => { var element = document.createElement('div'); element.innerHtml = _.join(['@', 'bang']); return element }); } getComponent().then(element => { document.body.appendChild(element); }); ``` 4. 安装babel-plugin-dynamic-import-webpack对异步加载模块语法进行翻译 ``` npm install @babel/plugin-syntax-dynamic-import --save-dev ``` 5. 在.babelrc配置文件中使用plugin-syntax-dynamic-import插件 ``` plugins: ["@babel/plugin-syntax-dynamic-import"] ``` #### SplitChunksPlugin配置参数详解 ``` /** 在webpack.config.js文件中 **/ optimization: { splitChunks: { /** all:-- 所有模块都分割 **/ /** async -- 只对异步加载的模块进行分割 **/ /** initial -- 只对同步模块进行分割 **/ chunks: 'all', minSize: 30000, /** 大于30000b(30kb)才会分割 **/ maxSize: 0, /** 一般不配置,会对大文件再次分割 **/ minChunks: 1, /** 引用这个模块超过一次就会分割 **/ maxAsyncRequests: 5, /** 同时只能加载分割5个库 **/ maxInitialRequests: 3, /** 最多分割出3个文件 **/ automaticNameDelimiter: "~", /** 文件名连接符 **/ name: true cacheGroups: { /** 同步模块加载必需配置 vendors: { test: /[\\/]node_modules[\\/]/, /** 如果模块同时满足cacheGroups和default, **/ /** 根据priority来决定模块的归属谁的值大属于谁 **/ priority: -10, filename: 'vendors.js' /** 打包成的文件名 **/ }, /** 同步非node_modules里面的模块配置专用 **/ default: { priority: -10, /** 已经打包过的模块忽略 **/ /** 不再打包分割 **/ reuseExistingChunk: true, filename: 'common.js' } } } } ``` #### lazy loading 懒加载异步模块 1. 打包分析工具 首先在打包的时候生成josn格式的描述文件,然后在package.json中添加命令 ``` scripts: { "dev-build": "webpack --profile --json > stats.json --config webpack.dev.js" } ``` 使用:`npm run dev-build` 生成的.stats.json文件可以用来分析 #### 使用Preloading, 空闲时候,静默加载我们的异步模块 1. 查看js文件中代码的使用率,打开控制台 command + shift + p 2. 搜索 Coverage 选中show Coverage 然后点击录制按钮变红后刷新页面 3. 在index.js中创建一个异步加载模块js的方法 ``` document.addEventListener('click', () => { import(/* webpackPrefetch: true */ './click.js').then(({default: func}) => { func(); }); }); ``` 正常情况下呢,我们只有在点击的时候,才加载click.js,但是配置了`Prefetch: true`,就会空闲时主动加载我们的click.js。空闲时主动加载解决了异步加载慢的问题,因为文件加载过了,再点击加载就会使用缓存的文件。 #### css文件代码分割 1. webpack 用style-loader处理的css会放到文件标签中 2. 使用mini-css-extract-plugin把css分割成单独文件 ``` npm install --save-dev mini-css-extract-plugin ``` 3. 这个插件不支持热更新(截止目前),所以一般是生产环境才用 4. 在webpack.prod.js中 将style-loader替换为MiniCssExtractPlugin.loader ``` const MiniCssExtractPlugin = require('mini-css-extract-plugin'); plugins: [ new MiniCssExtractPlugin({}); ] module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] ] } ``` 5. 过滤Tree Shakking, 在package.json中添加配置 ``` "sideEffects": ["*.css"] ``` 6. 配置filename和chunkFilename, 在webpack.prod.js中 ``` plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].chunk.css' }); } ``` 7. 使用optimize-css-assets-webpack-plugin对css合并和压缩 ``` npm install --save-dev optimize-css-assets-webpack-plugin ``` 8. 在webpack.prod.js中使用optimize-css-assets-webpack-plugin ``` const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); optimization: { minimizer: [new OptimizeCssAssetsPlugin({})] } ``` #### 多个入口文件引用的css打包到一个css文件 1. 借助splitChunks, 在webpack.prod.js中添加配置 ``` optimization: { splitChunks: { cacheGroups: { styles: { name: 'styles', test: /\.css$/, chunks: 'all', enforce: true } } } } ``` 2. 还可以把一个入口文件引入的所有css打包到对应的一个文件。这样每个入口引用的css,就会生成对应一个css文件。这个配置也借助splitChunks,在webpack.prod.js中配置 ``` optimization: { splitChunks: { cacheGroups: { mainStyles: { name: 'main', test: (m,c,entry = 'main') => m.constructor.name === 'CssModule && recursiveIssuer(m) === entry, chunks: 'all', enforce: true } } } } ``` #### webpack与浏览器缓存 1. 在webpack.prod.js中配置contenthash ``` output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' } ``` contenthash在文件没有改变时,它不会变,对应的文件有改变它的值就会变,这样浏览器加载的就是新文件 #### 通过webpack.ProvidePlugin插件自动帮我们引用没有import的模块 1. 在webpack.common.js中配置ProvidePlugin ``` const webpack = request('webpack'); plugins: [ new webpack.ProvidePlugin({ $: 'jquery' }) ] ``` 解决问题: 有的模块使用的是jquery,但是没有import jquery,`$`对象找不到,只在首页引入jquery是不行的,这时候借助webpack.ProvidePlugin,帮我们在使用`$`对象的模块引入jquery。 #### 让this都指向window 1. 安装 imports-loader ``` npm install imports-loader --save-dev ``` 2. 在webpack.common.js中添加配置 ``` module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'babel-loader', }, { loader: 'imports-loader?this=>window' }] }] } ``` #### 环境变量的使用 1. 在webpack.common.js中使用Env ``` comst merge = require('webpack-merge'); const prodConfig = require('./webpack.prod.js); const devConfig = require('./webpack.dev.js'); module.exports = (env) => { if (env && env.production) { return merge(commonConfig, prodConfig); } else { return merge(commonConfig, devConfig); } } ``` 2. 在package.json中修改命令 ``` "scripts": { "dev-build": "webpack -- config ./webpack.common.js", "build": "webpack --env.production --config ./webpack.common.js" } ``` #### Library的打包 1. 创建一个自己的包模块library ``` npm init -y ``` 2. 在包目录下新文件src/math.js 和 src/string.js 3. 在src/math.js中编写代码 ``` export function add(a, b) { return a + b; } export function minus(a, b) { return a - b; } export function multiply(a, b) { return a * b; } export function division(a, b) { return a / b; } ``` 4. 在src/string.js中编写代码 ``` export function join(a, b) { return a + " " + b; } ``` 5. 在src/index.js中编写代码 ``` import * as math from './math.js'; import * as string from './string'; export default { math, string } ``` 6. 在项目中安装webpack 和 webpack-cli ``` npm install webpack webpack-cli --save ``` 7. 在项目中创建webpack.config.js ``` const path = require('path'); module.exports = { mode: 'production', entry: './src/index.js' output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', library: 'library', libraryTarget: 'umd' } } ``` 8. 在package.js中配置入口文件和打包命令 ``` main: './src/index.js', scripts: { "build": "webpack" } ``` 9. 别人使用我们的库可能用到的引入方式 1、 `import library from 'library';` 2、` const library = require('library');` 3、 `require(['library'], function() {});` 4、 `` 10. 通过在webpack.config.js中配置`libraryTarget: 'umd'`可以使用前面三种引用 11. 通过在webpack.config.js中配置`library: 'library'`可以使用`