本篇博文的内容根据 入门 Webpack,看这篇就够了 该篇文章总结而来,其代码、模块示例、功能拓展部分均有所删减,若是想了解更多关于 WebPack 的详细内容,敬请参考原文
在 WebPack 模块化打包工具(上) 这篇文章当中,我们已经能成功使用 webpack 打包了文件,并配置了devtool
和devserver
选项,在这篇文章当中,我们将介绍更多关于 webpack 的用法
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
代码语言:javascript复制// 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
代码语言:javascript复制{
"greetText": "Love and Peace from JSON!"
}
代码语言:javascript复制//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
代码语言:javascript复制// 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/
}
]
}
}
代码语言:javascript复制// .babelrc
{
"presets": ["env", "react"]
}
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
代码语言:javascript复制// 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 的类名传递到组件的代码中,这样做有效避免了全局污染
代码语言:javascript复制// 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"
}
]
}
]
}
}
代码语言:javascript复制// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
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
代码语言:javascript复制// .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
代码语言:javascript复制// 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 上了,有需要的同学可自行下载