在开始以前检查node版本,确保node是最新的版本。使用旧版本,你可能遇到各类问题,由于它们可能缺乏 webpack 功能以及/或者缺乏相关 package 包。css
// 检查node版本 node -v
v8.11.1
mkdir webpack-demo && cd webpack-demo
首先建立一个文件夹webpack4.0-demo,并初始化npm,而后本地安装webpack和webpack-cli。html
mkdir webpack4.0-demo && cd webpack4.0-demo // 初始化npm,并所有使用默认值 npm init -y
使用4+版本须要安装webpack-cli,node
cnpm install --save-dev webpack webpack-cli
src/indexwebpack
要在 index.js
中打包 lodash
依赖,咱们须要在本地安装 library:git
cnpm install --save-dev lodash
import _ from 'lodash' function creatEl(){ let el = document.createElement('div'); // Lodash(目前经过一个 script 脚本引入)对于执行这一行是必需的 el.innerHTML = _.join(['Hello', 'webpack'], ' '); return el } document.body.appendChild(creatEl())
dist/index.htmlgithub
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>webpack</title> </head> <body> <script src="./main.js"></script> </body> </html>
package.jsonweb
{ "name": "webpack4.0-demo", "version": "1.0.0", "description": "", "private": true, // 删除入口文件,确保咱们安装包是 "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "lodash": "^4.17.11", "webpack": "^4.19.0", "webpack-cli": "^3.1.0" } }私有的(private)
执行webpack或者npx webpack
,会将脚本做为入口文件,而后输出为main.js。Node 8.2+ 版本提供的 npx
命令,能够运行在初始安装的 webpack 包(package)的 webpack 二进制文件。正则表达式
在浏览器中打开 index.html
,若是一切访问都正常,你应该能看到如下文本:'Hello webpack'。express
在 webpack 4 中,能够无须任何配置使用,然而大多数项目会须要很复杂的设置,这就是为何 webpack 仍然要支持 配置文件。这比在终端(terminal)中手动输入大量命令要高效的多,因此让咱们建立一个取代以上使用 CLI 选项方式的配置文件。npm
在主目录先添加一个webpack.config.js文件。
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } }
package.json
{ "name": "webpack4.0-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" // 新加代码 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "lodash": "^4.17.11", "webpack": "^4.19.0", "webpack-cli": "^3.1.0" } }
将index.html中的script标签的src改为bundle.js。如今,可使用 npm run build
命令,来替代咱们以前使用的 npx
命令。注意,使用 npm 的 scripts
,咱们能够像使用 npx
那样经过模块名引用本地安装的 npm 包。
为了从 JavaScript 模块中 import
一个 CSS 文件,须要在 module 安装并添加 style-loader 和 css-loader。
cnpm install --save-dev css-loader style-loader
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] } }
webpack 根据正则表达式,来肯定应该查找哪些文件,并将其提供给指定的 loader。在这种状况下,以 .css 结尾的所有文件,都将被提供给style-loader和css-loader。
这使你能够在依赖于此样式的文件中 import 'style.css'。如今,当该模块运行时,含有 CSS 字符串的 <style> 标签,将被插入到 html 文件的<head>中。
在src文件夹下添加一个style.css文件
src/style.css
.hello{
color: red;
}
src/index.js
import _ from 'lodash' import './style.css' function creatEl(){ let el = document.createElement('div'); // Lodash(目前经过一个 script 脚本引入)对于执行这一行是必需的 el.innerHTML = _.join(['Hello', 'webpack'], ' '); el.className = 'hello'; return el } document.body.appendChild(creatEl())
从新执行命令,在浏览器中打开index.html能够看到字体变成红色。
加载图片须要先安装file-loader
cnpm install --save-dev file-loader
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] } ] } }
src/index.js
import _ from 'lodash' import "./style.css" import Icon from './01.png' function creatEl(){ let el = document.createElement('div'); // Lodash(目前经过一个 script 脚本引入)对于执行这一行是必需的 el.innerHTML = _.join(['Hello', 'webpack'], ' '); el.className = 'hello'; let img = new Image(); img.src = Icon el.appendChild(img) return el } document.body.appendChild(creatEl())
file-loader 和 url-loader 能够接收并加载任何文件,而后将其输出到构建目录。这就是说,咱们能够将它们用于任何类型的文件,包括字体。让咱们更新 webpack.config.js
来处理字体文件:
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] }, { test: /\.(woff|woff2|eot|ttf|otf)/, use: [ 'file-loader' ] } ] } }
到目前为止,咱们在index.html文件中手动引入全部资源,然而随着应用程序增加,而且一旦对文件名使用哈希并输出多个bundle,手动的对index.HTML文件进行管理,一切就会变得困难起来。然而,咱们能够经过使用一些插件,会使这个过程变得更容易操控。
首先,要在src文件夹下新增一个print.js文件。
src/print.js
export default function print(){ console.log('hello webpack') }
src/index.js
import _ from 'lodash' import "./style.css" import Icon from './01.png'
import print from './print' function creatEl(){ let el = document.createElement('div'); // Lodash(目前经过一个 script 脚本引入)对于执行这一行是必需的 el.innerHTML = _.join(['Hello', 'webpack'], ' '); el.className = 'hello';
let btn = document.createElement('button'); btn.innerHTML = 'click me' btn.onclick = print;
let img = new Image(); img.src = Icon el.appendChild(img) el.appendChild(btn) return el } document.body.appendChild(creatEl())
webpack.config.js
安装 html-webpack-plugin 插件。
cnpm install --save-dev html-webpack-plugin
调整webpack.config.js文件
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { devtool: 'source-map', entry: { app: './src/index.js',
print: './src/print.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] }, { test: /\.(woff|woff2|eot|ttf|otf)/, use: [ 'file-loader' ] } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'webpack', // 生成的HTML文件的标题 template: path.resolve(__dirname, 'index.html') // 使用的模板路径 }) ] }
运行程序能够看到build文件夹下会生成一个index.html文件。全部的bundle文件都会被引入到index文件中。
因为常常修改,dist文件夹下会变得很是混乱webpack 会生成文件,而后将这些文件放置在 /dist
文件夹中,可是 webpack 没法追踪到哪些文件是实际在项目中用到的。一般,在每次构建前清理 /dist
文件夹,是比较推荐的作法,所以只会生成用到的文件。
首先安装插件:
cnpm install clean-webpack-plugin --save-dev
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { devtool: 'source-map', entry: { app: './src/index.js',
print: './src/print.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] }, { test: /\.(woff|woff2|eot|ttf|otf)/, use: [ 'file-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ // 用于生成的HTML文档的标题 title: 'webpack', // 使用的模板路径 template: path.resolve(__dirname, 'index.html') }) ] }
运行程序会看到先删除dist文件夹,而后会再次生成。
在使用webpack打包源代码时,会很难追踪到错误和警告在源代码中的原始位置。好比,将三个源文件(a.js、b.js和c.js)打包到一个bundle中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单的指向bundle.js。为了更容易的追踪错误和警告,咱们可使用source map功能,将编译后的代码映射回原始源代码。
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { devtool: 'source-map', entry: { app: './src/index.js',
print: './src/print.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] }, { test: /\.(woff|woff2|eot|ttf|otf)/, use: [ 'file-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 用于生成的HTML文档的标题 title: 'webpack', // 使用的模板路径 template: path.resolve(__dirname, 'index.html') }) ] }
src/print.js
export default function print(){ console.log('hello webpack'); console.error('error'); }
点击按钮能够看到
更多详细内容,查看devtool。
每次编译代码都要手动执行一次npm run build,这样变得很麻烦。webpack中有不一样几个选项,能够在代码发生变化时自动编译代码:
可是在大多数场景中使用的是webpack-dev-server。
添加一个用于启动 webpack 的观察模式的 npm script 脚本:
package.json
{ "name": "webpack4.0-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "watch": "webpack --watch", "build": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "express": "^4.16.3", "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", "lodash": "^4.17.11", "style-loader": "^0.23.0", "webpack": "^4.19.0", "webpack-cli": "^3.1.0" } }
如今,运行npm run watch,就会看到webpack编译代码后并不会退出。这是由于 script 脚本还在观察文件。如今,webpack 观察文件的同时,咱们先移除咱们以前引入的错误:
src/print.js
export default function print(){ console.log('hello webpack'); console.log('hello world'); }
而后刷新页面,能够看到:
惟一的缺点是,为了看到修改后的实际效果,你须要刷新浏览器。若是可以自动刷新浏览器就更好了,能够尝试使用 webpack-dev-server
,刚好能够实现咱们想要的功能。
webpack-dev-server
为你提供了一个简单的 web 服务器,而且可以实时从新加载(live reloading)。首先要安装插件
cnpm install --save-dev webpack-dev-server
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { devtool: 'source-map', entry: { app: './src/index.js', print: './src/print.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] }, { test: /\.(woff|woff2|eot|ttf|otf)/, use: [ 'file-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ // 用于生成的HTML文档的标题 title: 'webpack', // 使用的模板路径 template: path.resolve(__dirname, 'index.html') }) ], devServer: { contentBase: path.join(__dirname, 'dist') } }
以上配置告知 webpack-dev-server
,在 localhost:8080
下创建服务,将 dist目录下的文件,做为可访问文件。添加一个 script 脚本,能够直接运行开发服务器(dev server):
package.json
{ "name": "webpack4.0-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "watch": "webpack --watch", "start": "webpack-dev-server --open","build": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "express": "^4.16.3", "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", "lodash": "^4.17.11", "style-loader": "^0.23.0", "webpack": "^4.19.0", "webpack-cli": "^3.1.0", "webpack-dev-middleware": "^3.3.0", "webpack-dev-server": "^3.1.8" } }
如今,咱们能够在命令行中运行 npm start
,就会看到浏览器自动加载页面。若是如今修改和保存任意源文件,web 服务器就会自动从新加载编译后的代码。
webpack-dev-middleware
是一个容器(wrapper),它能够把 webpack 处理后的文件传递给一个服务器(server)。 webpack-dev-server
在内部使用了它,同时,它也能够做为一个单独的包来使用,以便进行更多自定义设置来实现更多的需求。接下来是一个 webpack-dev-middleware 配合 express server 的示例。
首先安装插件
cnpm install --save-dev express webpack-dev-middleware
接下来咱们须要对 webpack 的配置文件作一些调整,以确保中间件(middleware)功能可以正确启用:
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { devtool: 'source-map', entry: { app: './src/index.js', print: './src/print.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), publicPath: '/' }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(jpg|png|svg|gif)$/, use: [ 'file-loader' ] }, { test: /\.(woff|woff2|eot|ttf|otf)/, use: [ 'file-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ // 用于生成的HTML文档的标题 title: 'webpack', // 使用的模板路径 template: path.resolve(__dirname, 'index.html') }) ], devServer: { contentBase: path.join(__dirname, 'dist') } }
publicPath
也会在服务器脚本用到,以确保文件资源可以在 http://localhost:3000
下正确访问。而后在项目根目录下添加 server.js 文件,下一步就是设置咱们自定义的 express
服务:
server.js
const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const app = express(); const config = require('./webpack.config.js'); const complier = webpack(config); app.use(webpackDevMiddleware(complier, { publicPath: config.output.publicPath })); app.listen(3000, function(){ console.log('server is running') })
package.json
{ "name": "webpack4.0-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "watch": "webpack --watch", "start": "webpack-dev-server --open", "server": "node server.js", "build": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "express": "^4.16.3", "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", "lodash": "^4.17.11", "style-loader": "^0.23.0", "webpack": "^4.19.0", "webpack-cli": "^3.1.0", "webpack-dev-middleware": "^3.3.0", "webpack-dev-server": "^3.1.8" } }
如今,在你的终端执行 npm run server,而后打开浏览器跳转到http://localhost:3000,将会看到webpack程序已经运行。
模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它容许在运行时更新各类模块,而无需进行彻底刷新。
启用此功能实际上至关简单。而咱们要作的,就是更新 webpack-dev-server 的配置,和使用 webpack 内置的 HMR 插件。咱们还要删除掉 print.js
的入口起点,由于它如今正被 index.js
模式使用。
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { devtool: 'source-map', entry: { app: './src/index.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), publicPath: '/' }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ // 用于生成的HTML文档的标题 title: 'webpack', // 使用的模板路径 template: path.resolve(__dirname, 'index.html') }), new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin() ], devServer: { contentBase: path.join(__dirname, 'dist'), hot: true } }
注意,咱们还添加了 NamedModulesPlugin
,以便更容易查看要修补(patch)的依赖。在起步阶段,咱们将经过在命令行中运行 npm start
来启动并运行 dev server。如今,咱们来修改 index.js
文件,以便当 print.js
内部发生变动时能够告诉 webpack 接受更新的模块
index.js
import _ from 'lodash' import print from './print' function creatEl(){ let el = document.createElement('div'); // Lodash(目前经过一个 script 脚本引入)对于执行这一行是必需的 el.innerHTML = _.join(['Hello', 'webpack'], ' '); el.className = 'hello'; let btn = document.createElement('button'); btn.innerHTML = 'click me' btn.onclick = print; el.appendChild(btn) return el } document.body.appendChild(creatEl()) if (module.hot) { module.hot.accept('./print.js', function(){ console.log('Accepting the updated printMe module!'); print(); }) }
如需查看webpack更多配置详情,前往webpack配置。