让我们从0开始,新建一个项目,在终端执行以下语句:
mkdir webpack-4-quickstart && cd webpack-4-quickstart npm init -y npm i webpack --save-dev npm i webpack-cli --save-dev修改代码 package.json 中 scripts 部分:
"scripts": { "build": "webpack" }现在,我们的 package.json 是这样的:
{ "name": "webpack-4-quickstart", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.14.0", "webpack-cli": "^3.0.8" } }此时,我们执行 npm run build, 会给出以下提示/错误:
error: 没有入口文件warning: 建议设置 mode 选项
为了解决第一个问题,我们尝试新建 src/index.js:
console.log(`I'm a entry point`);此时再次执行 npm run build,成功打包出了 dist/main.js,因此我们可以得知:
webpack4 more配置了 entry(入口) src/index.js 和output(出口) dist/main.js
当然,如果你想覆盖这个配置(比如修改为 ./foo/src/js/index.js),可以在 package.json 修改:
"scripts": { "dev": "webpack --mode development ./foo/src/js/index.js --output ./foo/main.js", "build": "webpack --mode production ./foo/src/js/index.js --output ./foo/main.js" }webpack4 之前,我们写一个项目起码会设置两种类型文件:
用于开发环境的webpack.dev.conf.js,定义 webpack 启动的服务器等用于生产环境的webpack.prod.conf.js,定义UglifyJSPlugin或其他配置等而 webpack4 的 mode 给出了两种配置:development 和 production。
我们修改 package.json 中 scripts 部分:
"scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" }我们分别执行 npm run dev 和 npm run build
执行 npm run dev 打包的是未压缩的代码,而 npm run build 是压缩后的代码。
生产模式下:启用了 代码压缩、作用域提升(scope hoisting)、 tree-shaking,使代码最精简开发模式下:相较于更小体积的代码,提供的是打包速度上的优化webpack 4 的零配置主要应用于:
entry 默认设置为 ./src/index.jsoutput 默认设置为 ./dist/main.jsproduction 和 development 两种模式项目搭建,我们对webpack的诉求是:
js的处理:转换 ES6 代码,解决浏览器兼容问题css的处理:编译css,自动添加前缀,抽取css到独立文件html的处理:复制并压缩html文件dist的清理:打包前清理源目录文件assets的处理:静态资源处理server的启用:development 模式下启动服务器并实时刷新用 babel 转换 ES6 代码
用 babel 转换 ES6 代码需要使用到 babel-loader ,我们需要安装一系列的依赖:
npm i babel-core babel-loader babel-preset-env --save-dev然后在根目录新建一个babel配置文件 .babelrc:
{ "presets": [ "env" ] }那么如何将配置用于webpack打包中?
新建一个 webpack 的配置文件在 npm scripts 中使用 --module-bind使用 webpack 的配置文件的方法:
新建 webpack.config.js:
module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] } }在 npm scripts 中配置的方法:
"scripts": { "dev": "webpack --mode development --module-bind js=babel-loader", "build": "webpack --mode production --module-bind js=babel-loader" }使用 babel-polyfill 解决兼容性问题
然而浏览器依然不支持一些语法的使用,导致兼容性问题,我们用 babel-polyfill 解决:
npm i babel-polyfill babel-plugin-transform-runtime --save-dev.babelrc 添加配置:
{ "presets": [ "env" ], "plugins": [ "transform-runtime" ] }最后在 webpack.config.js 中将 babel-polyfill 加到你的 entry 数组中:
const path = require('path'); module.exports = { entry: [ "babel-polyfill", path.join(__dirname, './src/index.js') ], // ... };webpack 并不会主动将你的css代码提取到一个文件,过去我们使用 extract-text-webpack-plugin,在webpack4中我们使用mini-css-extract-plugin来解决这个问题。
postcss-loader 用于添加浏览器前缀,相关配置我喜欢在根目录新建 postcss.config.js 配置
npm i mini-css-extract-plugin css-loader --save-dev npm i style-loader postcss-loader --save-dev // webpack.config.js const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = (env, argv) => { const devMode = argv.mode !== 'production' return { module: { rules: [ // ..., { test: /\.css$/, use: [ devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] }, ] }, plugins: [ // ..., new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }) ] } } // postcss.config.js module.exports = { plugins: { autoprefixer: {} } }每次打包,都会生成项目的静态资源,随着某些文件的增删,我们的 dist 目录下可能产生一些不再使用的静态资源,webpack并不会自动判断哪些是需要的资源,为了不让这些旧文件也部署到生产环境上占用空间,所以在 webpack 打包前最好能清理 dist 目录。
npm install clean-webpack-plugin --save-dev const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { plugins: [ new CleanWebpackPlugin(['dist']), ] };package.json
"scripts": { "start": "webpack-dev-server --mode development --open", "build": "webpack --mode production" }现在我们模仿 create-react-app 的结构,自己搭建一个 react 项目,并且用less预编译:
├── public │ └── index.html # html 模板 ├── src │ ├── assets # 静态资源 │ │ └── logo.png │ ├── components # 组件 │ │ └── App.js │ ├── index.js # 入口文件 │ └── styles │ └── index.less ├── .babelrc ├── package-lock.json ├── package.json ├── postcss.config.js └── webpack.config.js在以上的基础(项目搭建部分),再安装react相关模块及less模块:
npm i react react-dom --save npm i babel-preset-react --save-dev npm i less less-loader --save-dev修改 .babelrc:
{ "presets": ["env", "react"] }修改 webpack.config.js :
// webpack.config.js const path = require('path'); module.exports = (env, argv) => { const devMode = argv.mode !== 'production' return { entry: [ "babel-polyfill", path.join(__dirname, './src/index.js') ], devServer: { port: 3000, //端口号 }, module: { rules: [ // ... // 处理react { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, // 处理less { test: /\.less$/, use: [ devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader', ] } ] } } };基本上搭建完这个项目了,如果你想看完整代码
同样地,我们模仿 vue-cli 的结构,自己搭建一个 vue 项目,这次我们的css预编译语言用 scss:
├── public │ └── index.html # html 模板 ├── src │ ├── assets # 静态资源 │ │ └── logo.png │ ├── components # 组件 │ │ └── App.vue │ ├── main.js # 入口文件 │ ├── main.js # 入口文件 │ └── styles │ └── index.scss ├── .babelrc ├── package-lock.json ├── package.json ├── postcss.config.js └── webpack.config.js在以上的基础(项目搭建部分),再安装vue相关模块及sass模块:
npm i vue --save npm i vue-loader vue-template-compiler --save-dev npm i node-sass sass-loader --save-dev // webpack.config.js const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = (env, argv) => { const devMode = argv.mode !== 'production' return { entry: [ "babel-polyfill", path.join(__dirname, './src/main.js') ], module: { rules: [ // ... // 解析vue { test: /\.vue$/, loader: 'vue-loader', options: { loaders: {} } }, // 处理scss { test: /\.scss$/, use: [ devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader', ] } ] }, plugins: [ // ... new VueLoaderPlugin() ] } };一个简易的 vue-cli 也搭建完成,如果你想看完整代码