本文偏入门&实践,从零开始配置 Webpack; 实际项目开发,零配置是不存在的。
? 安装&快速开始
快速初始化配置文件 package.json
// npm i yarn -gyarn init -y// yarn init --yes// yarn init --yes=true // 即全部选项默认为 yes
接下来将 webpack
添加到 package.json
=> devDependencies
yarn add webpack -D
安装成功后,创建目录 src/index.js
并添加如下内容 (默认入口为 src
)
document.write("Hello webpack4!");
命令行输入:
webpack --mode=development
成功后显示,打开 dist
文件夹会看到 main.js
(默认输出到 dist
)
Hash: 771a2645c2d430fa3bb4Version: webpack 4.5.0Time: 128msBuilt at: 2020-4-10 03:14:23 Asset Size Chunks Chunk Namesmain.js 2.81 KiB main [emitted] mainEntrypoint main = main.js[./index.js] 34 bytes {main} [built]
--mode
模式 (必选,不然会有WARNING
),是webpack4
新增的参数选项,默认是production
-
--mode production
生产环境- 提供
uglifyjs-webpack-plugin
代码压缩 - 不需要定义
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") })
默认production
- 默认开启 NoEmitOnErrorsPlugin ->
optimization.noEmitOnErrors
, 编译出错时跳过输出,以确保输出资源不包含错误 - 默认开启 ModuleConcatenationPlugin ->
optimization.concatenateModules
, webpack3 添加的作用域提升(Scope Hoisting)
- 提供
-
--mode development
开发环境- 使用 eval 构建 module, 提升增量构建速度
- 不需要定义
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
默认development
- 默认开启 NamedModulesPlugin ->
optimization.namedModules
使用模块热替换(HMR)时会显示模块的相对路径
接下来创建 dist/index.html
并引入 main.js
, 浏览器中打开看内容。
webpack-simple
再创建一个文件 src/content.js
, 在 src/index.js
中引入该模块
// content.jsmodule.exports = 'Looooooooooooooong content!';
// index.jsdocument.write(`Hello webpack4!${require('./content.js')}`);
再次执行 webpack --mode=development
完了打开 index.html
// 内容Hello webpack4!Looooooooooooooong content!
? 快速出土 webpack.config.js
安装 webpack-cli
来初始化配置
yarn add webpack-cli -D
webpack-cli init1. Will your application have multiple bundles? No // 单入口 string, 多页面 object2. Which module will be the first to enter the application? [example: './src/index'] ./src/index // 程序入口3. What is the location of "app"? [example: "./src/app"] './src/index' // 程序主文件4. Which folder will your generated bundles be in? [default: dist]: // 输出目录,默认 dist5. Are you going to use this in production? No // (Yes 第9步默认'config', No 则为 'prod')6. Will you be using ES2015? Yes // 会添加 ES6 => ES5 的配置7. Will you use one of the below CSS solutions? CSS // 选一种样式语言,会生成对应的 loader 配置8. If you want to bundle your CSS files, what will you name the bundle? (press enter to skip) // 回车跳过9. Name your 'webpack.[name].js?' [default: 'config']: // webpack.config.jsCongratulations! Your new webpack configuration file has been created!
配置生成OK,如下
// webpack.config.jsconst webpack = require('webpack');const path = require('path');const UglifyJSPlugin = require('uglifyjs-webpack-plugin');module.exports = { entry: './src/index.js', output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: ['env'] } }, { test: /\.css$/, use: [ { loader: 'style-loader', options: { sourceMap: true } }, { loader: 'css-loader' } ] } ] }, plugins: [new UglifyJSPlugin()] // 这款插件用于压缩 JS 代码,减少资源体积大小};
再度执行编译一切OK, 打开 index.html
查看内容
webpack --mode=developmentHash: c30d4f489db4d568ee0bVersion: webpack 4.5.0Time: 1308msBuilt at: 2020-4-11 04:14:23Asset Size Chunks Chunk Namesapp.38de904fed135db4bf0a.js 1.17 KiB app [emitted] appEntrypoint app = app.38de904fed135db4bf0a.js[./src/content.js] 62 bytes {app} [built][./src/index.js] 80 bytes {app} [built]
接下来就是在这份配置上,做一些实践。
? 使用 html-webpack-plugin
创建 html 文件
- 该插件简化了创建 HTML 文件的创建,服务于 webpack bundle。
- 解决的问题:每次编译完成后不用再去手动修改
index.html
, 它会与 JS 生成在同一目录dist
并引入app.38de904fed135db4bf0a.js
。
yarn add html-webpack-plugin -D
安装完成后,在 webpack.config.js
下配置
// webpack.config.js+ const HtmlWebpackPlugin = require('html-webpack-plugin');plugins: [ new UglifyJSPlugin(),+ new HtmlWebpackPlugin({ title: 'webpack-cli' }),]
重新执行 webpack --mode=development
, dist
目录就会多个 index.html
并引入了 main.bundle.js
.
⚛️ Webpack4 配置 React 开发环境
上面配置中的 module.rules
的应用
babel-loader
将 ES6* 代码转化为 ES5 代码Babel 默认只转换新的 JavaScript 句法 (
syntax
), 而不转换新的 API, 比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise
等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转码。举例来说,ES6 在
Array
对象上新增了Array.from
方法。Babel
就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill
,为当前环境提供一个垫片。—— 摘自
yarn add react react-dom babel-preset-react
babel-preset-react
用于解析react
的语法;
babel-preset-env
初始化配置时已经安装。它的前身是babel-preset-es2015/es2016/es2017
以后要用新特性这个包就可以搞定一切。
安装完成,修改 src/index.js
的内容为
import React from 'react';import { render } from 'react-dom';render(Hello world!
, document.querySelector('#root'));
把 webpack.config.js module.rules
babel-loader
配置 presets
删掉。
.babelrc
文件,内容如下 // .babelrc{ "presets": [ "env", "react" ]}
// webpack.config.jsplugins: [ new HtmlWebpackPlugin({+ template: './index.html' // 添加模版文件 }),]
// index.htmlWebpack4-react16
再次执行 webpack --mode=development
, ok!
? 实时刷新页面
yarn add webpack-dev-server -D
打开 package.json
添加构建脚本
-
--open
自动打开浏览器并定向至http://localhost:8080/
"scripts": { "dev": "webpack-dev-server --mode=development --open --hot" "//": "webpack-dev-server --mode=development --host 0.0.0.0" "//": "使用本机 IP 访问项目 [Your IP]:8080 => 192.168.0.111:8080"},
执行 yarn dev
, 自动刷新完成。
? 模块热替换
即在不重载页面的情况下,实时替换更新修改的模块。提高开发效率。 本文使用 React, 所以用
yarn add react-hot-loader -D
项目根目录下新建文件 .babelrc
, 添加内容:
{+ "plugins": ["react-hot-loader/babel"]}
在 src
目录下添加文件 App.js
// src/App.jsimport React from 'react';import { hot } from 'react-hot-loader';const App = () =>Hello World!;export default hot(module)(App)
应用入口引入 App.js
// src/index.jsimport React from 'react';import { render } from 'react-dom';import App from './App';render(, document.querySelector('#root'));
重新执行 yarn dev
, 修改下 App.js
的代码,注意看浏览器与 console
.
[HMR] - ./src/App.jslog.js:24 [HMR] App is up to date.
如果 hot(module)(App)
与 render
一个文件则会收到警告
? Webpack4 加载 CSS
在 4.x 版本之前,用的是extract-text-webpack-plugin
,不过 webpack@4.3.0 不支持使用。
yarn add mini-css-extract-plugin -D
// module.rules{ test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { } } ]}plugins: [ new MiniCssExtractPlugin({ filename: "[name].[contenthash].css", chunkFilename: "[id].[contenthash].css" })],
? 按需加载 React 组件
yarn add react-loadableyarn add babel-preset-stage-2 -D // for 动态 import() 语法
import Loadable from 'react-loadable';const Loading = () => 'Loading...';const Home = Loadable({ loader: () => import('./Home'), loading: Loading });
效果如图
按需加载OK,不过发现个问题,这个 Header
组件被多处调用,样式&JS都存在多次加载。
接下来要做的就是把共用的代码提取出来。
? Webpack4 提取公共 CSS&JS
配置如下
// webpack.config.jsoptimization: { splitChunks: { cacheGroups: { commons: { name: 'commons', priority: 10, chunks: 'initial' }, styles: { name: 'styles', test: /\.css$/, chunks: 'all', minChunks: 2, enforce: true } } }}
? 分离第三方库
entry: { app: './src/index.js',+ ramda: ['ramda'],}new HtmlWebpackPlugin({ template: './index.html',+ chunks: ['app', 'commons', 'ramda']})2.e9dc7e430f6a31c868b2.css 45 bytes 2 [emitted] app.bundle.js 9.6 KiB app [emitted] app 0.decbf5b19337a4ce4aac.css 61 bytes 0 [emitted] 0.bundle.js 4.01 KiB 0 [emitted]+ ramda.bundle.js 7.99 KiB ramda [emitted] ramda index.html 393 bytes [emitted]
?
yarn add antdyarn add less less-loader babel-plugin-import -D
// .babelrc 添加{ "plugins": [ [ "import", { "style": true, "libraryName": "antd" } ] ]}
// webpack.config.js module.rules 添加{ test: /\.less$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'less-loader', options: { sourceMap: true, javascriptEnabled: true, modifyVars: { 'primary-color': '#531dab' } } } ]}
? autoprefixer 处理浏览器前缀
display: -webkit-box;display: -ms-flexbox;display: flex;
yarn add autoprefixer postcss-loader -D
项目根目录新建 postcss.config.js
// postcss.config.jsmodule.exports = { plugins: [ require('autoprefixer')({ 'browsers': ['> 1%', 'last 2 versions'] }) ]};
// webpack.config.js module.rules{ test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader',+ 'postcss-loader' ]}
? 一些报错 & 解决办法
Uncaught Error: [HMR] Hot Module Replacement is disabled.
运行 webpack-dev-server --mode=development
报错。
把 webpack.config.js devSever
hot: true, inline: true
删掉,
webpack-dev-server --mode=development --hot --inline
或者
plugins: [+ new webpack.HotModuleReplacementPlugin(),]
ERROR in 0.js from UglifyJs TypeError: Cannot read property 'sections' of null
TypeError: Cannot read property 'sections' of null ? Remove `new UglifyJsPlugin` from plugins partschema id ignored LoaderOptionsPlugin ? Remove `new LoaderOptionsPlugin` plugin from configschema id ignored SourceMapDevToolPlugin ? Remove `devtool` config
ERROR in chunk app [entry] [name].[chunkhash].js
? 参考资料
挤时间敲了几天,教程终于告一段落!后续还会继续完善其他的配置(HappyPack, DllReferencePlugin...)实践; 本文如有错误,欢迎指正,非常感谢。