WebPack 模块化打包工具(下)
本篇博文的内容根据 入门 Webpack,看这篇就够了 该篇文章总结而来,其代码、模块示例、功能拓展部分均有所删减,若是想了解更多关于 WebPack 的详细内容,敬请参考原文
在 WebPack 模块化打包工具(上) 这篇文章当中,我们已经能成功使用 webpack 打包了文件,并配置了 devtool 和 devserver 选项,在这篇文章当中,我们将介绍更多关于 webpack 的用法
1. Loaders
通过使用不同的 Loaders,webpack 有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换 scss 为 css,或者把 ES6 或 TS 文件转换为现代浏览器兼容的 JS 文件,对 React 的开发而言,合适的 Loaders 可以把 React 的中用到的 JSX 文件转换为 JS 文件
Loaders 需要单独安装并且需要在 webpack.config.js
中的 modules 关键字下进行配置,Loaders 的配置包括以下几方面:
- test:一个用以匹配 loaders 所处理文件的拓展名的正则表达式(必须)
- loader:loader的名称(必须)
- include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
- query:为 loaders 提供额外的设置选项(可选)
我们通过安装和配置 Babel 依赖包来进一步了解 Loaders 吧,我们需要安装拥有核心功能的 babel-core
包,解析 ES6 的 babel-env-preset
包和解析 JSX 的 babel-preset-react
包,键入以下命令一次完成安装
// npm一次性安装多个依赖模块,模块之间用空格隔开
npm i babel-core babel-loader babel-preset-env babel-preset-react -D
安装完成之后,我们需要在 webpack 文件中配置 Babel
// webpack.config.js
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true //实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}
]
}
}
现在 webpack 的配置已经允许我们使用 ES6 以及 JSX 的语法了,我们也是使用之前的例子进行测试,不过这次我们会使用到 React,所以还需要安装一下 React 的依赖包,并在 app
文件夹下新建 config.json
的文件
npm i react react-dom -D
{
"greetText": "Love and Peace from JSON!"
}
//Greeter.js
import React, {Component} from 'react'
import config from './config.json';
class Greeter extends Component{
render() {
return (
<div>
{config.greetText}
</div>
);
}
}
export default Greeter
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
render(<Greeter />, document.getElementById('root'));
运行 npm satart
命令进行编译打包,再运行 npm run server
命令启动本地服务器
虽然我们当前项目中 Babel 的配置选项很少,完全可以写在 webpack.config.js
文件当中,当实际项目中,我们是会对 Babel 进行各种各样的配置的,这时候就不适合继续写在 webpack.config.js
文件中了,所以我们将 Babel 的配置独立到一个 .babelrc
文件中,webpack 会自动读取 .babelrc
文件里的 Babel 配置选项
// webpack.config.js
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true //实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
}
]
}
}
// .babelrc
{
"presets": ["env", "react"]
}
2. CSS Modules
JavaScript 模块化处理相信大家已经很熟悉了,而 CSS 同样也能进行模块化处理,webpack 提供的 css-loader
和 style-loader
可以对样式表进行处理,css-loader
使你能够使用类似 @import
和 url(...)
的方法实现 require()
的功能,style-loader
将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入 webpack 打包后的 JS 文件中
npm i style-loader css-loader -D
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
}
]
}
]
}
};
注意这里对同一个文件引入多个 loader 的方法
随后,我们在 app
文件夹里创建一个名字为 main.css
的文件,并设置如下样式
/* main.css */
html {
box-sizing: border-box;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5, h6, p, ul {
margin: 0;
padding: 0;
}
#root {
color: blue;
}
我们项目中用到的 webpack 只有单一的入口,其它的模块需要通过 import
, require
, url
等方式与入口文件建立其关联,为了让 webpack 能找到 main.css
文件,我们需要把它导入 main.js
中
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css'; //使用require导入css文件
render(<Greeter />, document.getElementById('root'));
运行结果
Webpack 对 CSS 模块化提供了非常好的支持,只需要在 CSS loader中进行简单配置即可,然后就可以直接把 CSS 的类名传递到组件的代码中,这样做有效避免了全局污染
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}
]
}
]
}
};
我们在 app
文件夹下创建一个 Greeter.css
文件来进行一下测试
/* Greeter.css */
.root {
background-color: #eee;
padding: 10px;
border: 3px solid #ccc;
}
导入 .root
到 Greeter.js
中
//Greeter.js
import React, {Component} from 'react'
import config from './config.json';
import styles from './Greeter.css'; //导入
class Greeter extends Component{
render() {
return (
<div className={styles.root}>
{config.greetText}
</div>
);
}
}
export default Greeter
运行结果
关于 CSS Modules 的更多用法,可以去 官方文档 了解更多
我们再介绍一个日常开发里经常用到的 CSS 处理器——PostCSS,首先安装 postcss-loader
和 autoprefixer
(自动添加前缀的插件)
npm i postcss-loader autoprefixer -D
同样的,也是在 webpack 配置文件中添加 postcss-loader
,在根目录新建 postcss.config.js
文件,添加如下代码之后,重新使用 npm start
打包时,我们写的 CSS 就会自动根据 Can i use 里的数据添加不同前缀了
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
},
{
loader: "postcss-loader"
}
]
}
]
}
}
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
3. Plugins
Plugins 是用来拓展 Webpack 功能的,它们会在整个构建过程中生效,执行相关的任务,Loaders 和 Plugins 常常被弄混,Loaders 是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,Plugins 并不直接操作单个文件,它直接对整个构建过程其作用
继续运行上面的例子,我们给项目添加几个常用的插件,HtmlWebpackPlugin
这个插件的作用是依据一个简单的 index.html
模板,生成一个自动引用你打包后的 JS 文件的新 index.html
,这在每次生成的 JS 文件名称不同时非常有用(比如添加了 hash
值)
npm i html-webpack-plugin -D
移除 public
文件夹,此插件可自动生成 index.html
文件,在 app
目录下,创建一个 index.tmpl.html
文件模板,这个模板包含 title
等必须元素,在编译过程中,插件会依据此模板生成最终的 HTML 页面,会自动添加所依赖的 CSS, JS, favicon 等文件
<!-- index.tmpl.html -->
<!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>Document</title>
</head>
<body>
<div id='root'></div>
</body>
</html>
更新 webpack 的配置文件,并新建一个 build
文件夹用来存放最终的输出文件
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
module: {
...
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
})
]
}
运行结果
Hot Module Replacement
(HMR)属于 webpack 插件,该插件允许你在修改组件代码后,自动刷新实时预览修改后的效果,我们需要在 webpack 中做两项配置,在 webpack 配置文件中添加 HMR 插件;在 webpack Dev Server中添加 hot
参数
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true, //实时刷新
hot: true
},
module: {
...
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
}),
new webpack.HotModuleReplacementPlugin() //热加载插件
]
}
Babel 有一个叫做 react-transform-hrm
的插件,可以在不对 React 模块进行额外的配置的前提下让 HMR 正常工作
npm i babel-plugin-react-transform react-transform-hmr -D
配置 Babel
// .babelrc
{
"presets": ["react", "env"],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
}
我们再来看看其他插件,OccurenceOrderPlugin
为组件分配 ID,通过这个插件 webpack 可以分析和优先考虑使用最多的模块,并为它们分配最小的 ID;UglifyJsPlugin
压缩 JS 代码;ExtractTextPlugin
分离 CSS 和 JS 文件
npm i extract-text-webpack-plugin -D
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
...
module: {
...
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
}),
new webpack.HotModuleReplacementPlugin(), //热加载插件
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("/app/main.css")
]
}
运行结果
该章节的内容到这里就全部结束了,源码我已经发到了 GitHub WebPack_2 上了,有需要的同学可自行下载
End of File
行文过程中出现错误或不妥之处在所难免,希望大家能够给予指正,以免误导更多人,最后,如果你觉得我的文章写的还不错,希望能够点一下喜欢和关注,为了我能早日成为简书优秀作者献上一发助攻吧,谢谢!^ ^