webpack 之能处理js,如果需要处理其他格式就需要引入loader 去处理
什么是Loader webpack可以使用 loader 来预处理文件,就是通过使用不同的Loader,webpack可以把不同的静态文件都编译成js文件,比如css,sass,less,ES6/7,vue,JSX等。
加载 CSS 为了从 JavaScript 模块中 import 一个 CSS 文件,你需要在 module 配置中 安装并添加 style-loader 和 css-loader :
1 npm install --save-dev style-loader css-loader
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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> 中。
我们尝试一下,通过在项目中添加一个新的 style.css 文件,并将其导入到我们的 index.js 中:
project
1 2 3 4 5 6 7 8 9 10 webpack-demo |- package.json |- webpack.config.js |- /dist |- bundle.js |- index.html |- /src + |- style.css |- index.js |- /node_modules
src/style.css
src/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import _ from 'lodash'; + import './style.css'; function component() { var element = document.createElement('div'); // lodash 是由当前 script 脚本 import 导入进来的 element.innerHTML = _.join(['Hello', 'webpack'], ' '); + element.classList.add('hello'); return element; } document.body.appendChild(component());
现在运行构建命令:
再次在浏览器中打开 index.html,你应该看到 Hello webpack 现在的样式是红色。要查看 webpack 做了什么,请检查页面(不要查看页面源代码,因为它不会显示结果),并查看页面的 head 标签。它应该包含我们在 index.js 中导入的 style 块元素。
请注意,在多数情况下,你也可以进行 CSS 分离 ,以便在生产环境中节省加载时间。最重要的是,现有的 loader 可以支持任何你可以想到的 CSS 处理器风格 - postcss , sass 和 less 等。
加载图片 1 npm install --save-dev file-loader
添加 file-loader
1 2 3 4 5 6 { test : /\.(png|svg|jpg|gif)$/ , use : [ 'file-loader' ] }
将小图片转换成base64格式 需要安装 url-loader:当图片小于limit的时候会把图片BASE64编码,大于limit参数的时候还是使用file-loader 进行拷贝
1 npm install url-loader -D
在 webpack.config.js 里添加 loader 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 module.exports = { module: { rules: [ { test: /\.(png|jpg|gif|bmp/)$/i, use: [ { loader: 'url-loader', options: { name:'[name].[ext]', outputPath: 'images/', limit: 8192 //小于8192b,就可以转化成base64格式。大于就会打包成文件格式 } } ] } ] } }
详细请看官方文档:url-loader
支持加载样式SASS文件 在 webpack.config.js 里添加 loader 配置
1 2 3 4 5 6 7 8 9 10 11 12 module .exports = { module : { rules : [{ test : /\.scss$/ , use : [ "style-loader" , "css-loader" , "sass-loader" ] }] } };
官方很多的loader 需要自行取官网学习(copy)
plugin plugin : 可以在webpack运行到某个时刻的时候,帮你做一些事情
预先准备 首先,让我们调整一下我们的项目:
project
新建文件 src/print.js
src/print.js
1 2 3 export default function printMe ( ) { console .log ('I get called from print.js!' ); }
src/index.js 调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import _ from 'lodash'; import printMe from './print.js'; function component() { var element = document.createElement('div'); var btn = document.createElement('button'); element.innerHTML = _.join(['Hello', 'webpack'], ' '); btn.innerHTML = 'Click me and check the console!'; btn.onclick = printMe; element.appendChild(btn); return element; } document.body.appendChild(component());
我们还要更新 dist/index.html 文件,来为 webpack 分离入口做好准备:
dist/index.html
1 2 3 4 5 6 7 8 9 <!doctype html> <html> <head> <script src="./print.bundle.js"></script> </head> <body> <script src="./app.bundle.js"></script> </body> </html>
现在调整配置。我们将在 entry 添加 src/print.js 作为新的入口起点(print),然后修改 output,以便根据入口起点名称动态生成 bundle 名称:
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 const path = require ('path' );module .exports = { entry : { app : './src/index.js' , print : './src/print.js' }, output : { filename : '[name].bundle.js' , path : path.resolve (__dirname, 'dist' ) }
执行 npm run build
我们可以看到,webpack 生成 print.bundle.js 和 app.bundle.js 文件,这也和我们在 index.html 文件中指定的文件名称相对应。如果你在浏览器中打开 index.html,就可以看到输出了
但是每次改了入口的名称,index.html文件仍然会引用旧的名字。所以引用会报错, 怎么解决呢?可以采用 HtmlWebpackPlugin 插件来解决这个问题。
设定 HtmlWebpackPlugin 首先安装插件,并且调整 webpack.config.js 文件:
1 npm install --save-dev html-webpack-plugin
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const path = require('path'); + const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, + plugins: [ + new HtmlWebpackPlugin({ + title: 'Output Management' + template: 'src/index.html' //以index.html为模板,把打包生成的js自动引入到这个html文件中 + }) + ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
在我们构建之前,你应该了解,虽然在 dist/ 文件夹我们已经有 index.html 这个文件,然而 HtmlWebpackPlugin 还是会默认生成 index.html 文件。这就是说,它会用新生成的 index.html 文件,把我们的原来的替换。执行 npm run build:
如果你在代码编辑器中将 index.html 打开,你就会看到 HtmlWebpackPlugin 创建了一个全新的文件,所有的 bundle 会自动添加到 html 中。
如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和选项,那么你就应该多多熟悉 HtmlWebpackPlugin 仓库。
你还可以看一下 html-webpack-template ,除了默认模板之外,还提供了一些额外的功能。
清理 /dist 文件夹 插件 每次都要自己来上dist 文件夹,不爽,用插件啦
1 npm install clean-webpack-plugin --save-dev
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); + const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, plugins: [ + new CleanWebpackPlugin(['dist'], { + root: path.resolve(__dirname, '../'), //根目录 + }), new HtmlWebpackPlugin({ title: 'Output Management' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!
更多 插件请去官网学习 (copy)
使用 source map 当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到 bundle.js。这并通常没有太多帮助,因为你可能需要准确地知道错误来自于哪个源文件。
为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, + devtool: 'inline-source-map', plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Development' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
1 2 3 4 5 6 7 8 9 10 devtool : 'cheap-module-eval-source-map' ,
选择自动编译工具 webpack 中有几个不同的选项,可以帮助你在代码发生变化后自动编译代码:
webpack’s Watch Mode
webpack-dev-server
webpack-dev-middleware
多数场景中使用 webpack-dev-server,但是不妨了解多一点。
使用观察模式(了解) 这个比较简单,直接在package.json 添加script 即可
package.json
1 2 3 4 5 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "watch": "webpack --watch", "build": "webpack" }
现在,你可以在命令行中运行 npm run watch,就会看到 webpack 编译代码,然而却不会退出命令行。这是因为 script 脚本还在观察文件。
修改代码可以看到 webpack 自动重新编译修改后的模块!
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,可以尝试使用 webpack-dev-server,恰好可以实现我们想要的功能。并且我测试,连index.html 也不会重新生成. pass
WebpackDevServer使用(掌握) 这个是常用的
使用 WebpackDevServer 提升开发效率
webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)
1 npm install --save-dev webpack-dev-server
修改配置文件,告诉开发服务器(dev server),在哪里查找文件:
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, devtool: 'inline-source-map', + devServer: { + contentBase: './dist' + }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Development' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
以上配置告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。
让我们添加一个 script 脚本,可以直接运行开发服务器(dev server):
package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 { "name": "development", "version": "1.0.0", "description": "", "main": "webpack.config.js", "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.16", "css-loader": "^0.28.4", "csv-loader": "^2.1.1", "file-loader": "^0.11.2", "html-webpack-plugin": "^2.29.0", "style-loader": "^0.18.2", "webpack": "^3.0.0", "xml-loader": "^1.2.1" } }
现在,我们可以在命令行中运行 npm start,就会看到浏览器自动加载页面。如果现在修改和保存任意源文件,web 服务器就会自动重新加载编译后的代码。
使用 webpack-dev-middleware(了解) webpack-dev-middleware 是一个容器(wrapper),它可以把 webpack 处理后的文件传递给一个服务器(server)。 webpack-dev-server 在内部使用了它,同时,它也可以作为一个单独的包来使用,以便进行更多自定义设置来实现更多的需求。
首先,安装 express 和 webpack-dev-middleware:
1 npm install --save-dev express webpack-dev-middleware
接下来我们需要对 webpack 的配置文件做一些调整,以确保中间件(middleware)功能能够正确启用:
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, devtool: 'inline-source-map', plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Output Management' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), + publicPath: '/' } };
publicPath 也会在服务器脚本用到,以确保文件资源能够在 http://localhost:3000 下正确访问,我们稍后再设置端口号。下一步就是设置我们自定义的 express 服务:
project
1 2 3 4 5 6 7 8 9 webpack-demo |- package.json |- webpack.config.js + |- server.js |- /dist |- /src |- index.js |- print.js |- /node_modules
server.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const express = require ('express' );const webpack = require ('webpack' );const webpackDevMiddleware = require ('webpack-dev-middleware' );const app = express ();const config = require ('./webpack.config.js' );const compiler = webpack (config);app.use (webpackDevMiddleware (compiler, { publicPath : config.output .publicPath })); app.listen (3000 , function ( ) { console .log ('Example app listening on port 3000!\n' ); });
现在,添加一个 npm script,以使我们更方便地运行服务:
package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 { "name": "development", "version": "1.0.0", "description": "", "main": "webpack.config.js", "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.16", "css-loader": "^0.28.4", "csv-loader": "^2.1.1", "express": "^4.15.3", "file-loader": "^0.11.2", "html-webpack-plugin": "^2.29.0", "style-loader": "^0.18.2", "webpack": "^3.0.0", "webpack-dev-middleware": "^1.12.0", "xml-loader": "^1.2.1" } }
现在,在你的终端执行 npm run server,打开浏览器,跳转到 http://localhost:3000 修改后也谁要手动刷新
目前webpack-dev-server 支持自动刷新了(全局刷新),但是我们一些复杂页面并不要全局刷新,只需要加载局部刷新
模块热替换 模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。本页面重点介绍实现 ,而概念页面 提供了更多关于它的工作原理以及为什么它有用的细节。
HMR 不适用于生产环境,这意味着它应当只在开发环境使用。更多详细信息,请查看生产环境构建指南 。
启用 HMR 启用此功能实际上相当简单。而我们要做的,就是更新 webpack-dev-server 的配置,和使用 webpack 内置的 HMR 插件。我们还要删除掉 print.js 的入口起点,因为它现在正被 index.js 模块使用。
如果你使用了 webpack-dev-middleware 而没有使用 webpack-dev-server,请使用 webpack-hot-middleware package 包,以在你的自定义服务或应用程序上启用 HMR。
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); + const webpack = require('webpack'); module.exports = { entry: { - app: './src/index.js', - print: './src/print.js' + app: './src/index.js' }, devtool: 'inline-source-map', devServer: { contentBase: './dist', + hot: true }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Hot Module Replacement' }), + new webpack.NamedModulesPlugin(), + new webpack.HotModuleReplacementPlugin() ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
你可以通过命令来修改 webpack-dev-server 的配置:webpack-dev-server --hotOnly。
注意,我们还添加了 NamedModulesPlugin,以便更容易查看要修补(patch)的依赖。在起步阶段,我们将通过在命令行中运行 npm start 来启动并运行 dev server。
现在,我们来修改 index.js 文件,以便当 print.js 内部发生变更时可以告诉 webpack 接受更新的模块。
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import _ from 'lodash'; import printMe from './print.js'; function component() { var element = document.createElement('div'); var btn = document.createElement('button'); element.innerHTML = _.join(['Hello', 'webpack'], ' '); btn.innerHTML = 'Click me and check the console!'; btn.onclick = printMe; element.appendChild(btn); return element; } document.body.appendChild(component()); + 添加局部刷新文件代码 + if (module.hot) { + module.hot.accept('./print.js', function() { + console.log('Accepting the updated printMe module!'); + printMe(); + }) + }
更改 print.js 中 console.log 的输出内容即可以发现局部刷新了
但是这里修改index.js 还是全局刷新的
注意点:
引入css,用框架Vue,React 时,不需要写 module.hot.accept(),因为在使用css-loader,vue-loader,babel-preset时,就已经配置好了HMR,不需要自己写