Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。
Webpack
安装Webpack
webpack是建立在node.js环境下的,所以要使用它我们需要先安装node和npm,其相关知识这里将不介绍。
安装node.js
在终端输入以下命令即可
$ sudo apt-get install nodejs
安装npm
在终端输入以下命令即可
$ sudo apt-get install npm
为了保证下载速度,我们直接永久设置为淘宝源为npm的registry地址。在终端里输入
npm config set registry https://registry.npm.taobao.org
- 配置成功后输入以下命令来查看是否成功
npm config get registry
如图所示即为成功
安装webpack
- 全局安装
在终端输入
sudo npm install -g webpack
- 局部安装
进入项目目录,并在终端中输入
npm install --save-dev webpack
使用Webpack
- 创建一个工作目录,并进入这个目录创建一个项目,在终端输入以下命令,会自动生成一个package.json文件,这是一个标准的npm说明文件,里面包含了一些信息,包含了项目的依赖模块,自定义脚本任务等。
npm init
输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。
- 在本项目中安装Webpack作为依赖包,在终端输入以下命令
npm install --save-dev webpack
-
创建app和public文件夹
- app文件夹用来存放原始数据和我们将写的JavaScript模块
- public文件夹用来存放准备给浏览器读取的数据(包括使用webpack生成的打包后的js文件以及一个index.html文件)
-
创建几个html和js文件做一个简单的例子
- 在public下创建index.html文件,加载通过webpack打包之后的bundle.js文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Webpack Sample Project</title> </head> <body> <div id='root'> </div> <script src="bundle.js"></script> </body> </html>
- 在app目录下创建Greeter.js,用来返回一个简单的文字信息的html元素的函数
module.exports = function() { var greet = document.createElement('div'); greet.textContent = "Hi there and greetings!"; return greet; };
- 在app目录下创建main.js,用来把Greeter模块返回的结点插入页面
var greeter = require('./Greeter.js'); document.getElementById('root').appendChild(greeter());
使用webpack进行打包
基本命令为
webpack {entry file/入口文件} {destination for bundled file/存放bundle.js的地方}
只需要指定一个入口文件,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack没有进行全局安装,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中属于如下命令
node_modules/.bin/webpack app/main.js public/bundle.js
执行结果如下,可以看到帮我们打包了两个文件
打开浏览器访问index.html可以看到如下结果
-
webpack还有许多功能,通过命令行都可以实现,但是命令多了不好记也容易出错,所以webpack也提供了配置文件方式,在项目根目录下创建webpack.config.js文件,在其中编写我们所需要的配置。
module.exports = { //唯一入口文件 entry: __dirname + "/app/main.js", output: { //打包后的文件存放的地方 path: __dirname + "/public", //打包后输出文件的文件名 filename: "bundle.js" } }
注:__dirname是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
接下来指定打包命令只需要在终端中输入webpack即可。执行的结果和用命令一致。 打包操作还可以更简单,为了解决命令复杂且多的问题,npm还提供了引导任务执行的功能。对其进行配置后可以使用简单的npm start命令来代替这些繁琐的命令。在package.json中对npm的脚本部分进行相关设置即可,设置方法如下。
{
"name": "first_webpack_project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
//配置start命令
"start": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^3.4.1"
}
}
注:npm的start是一个特殊的脚本名称,它的特殊性表现在,在命令行中使用npm start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}如npm run build,以下是执行npm start后命令行的输出显示
Webpack功能
生成Source Maps
开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。
通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。
devtool选项 | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度 |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便 |
eval-source-map | 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项 |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点 |
在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:
devtool选项 | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度 |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便 |
eval-source-map | 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项 |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点 |
按照说明选择一种你希望的生成方式,在webpack.config.js中进行配置
module.exports = {
//配置生成Source Maps,选择合适的选项
devtool: "eval-source-map",
//唯一入口文件
entry: __dirname + "/app/main.js",
output: {
//打包后的文件存放的地方
path: __dirname + "/public",
//打包后输出文件的文件名
filename: "bundle.js"
}
}
使用webpack构建本地服务器
Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现监测你的代码的修改,并自动刷新修改后的结果,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖。安装命令如下
npm install --save-dev webpack-dev-server
devserver作为webpack配置选项中的一项,具有以下配置选项
devserver配置选项 | 功能描述 |
---|---|
contentBase | 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录) |
port | 设置默认监听端口,如果省略,默认为”8080“ |
inline | 设置为true,当源文件改变时会自动刷新页面 |
colors | 设置为true,使终端输出的文件为彩色的 |
historyApiFallback | 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html |
在webpack.config.js中进行配置devserver:
module.exports = {
//配置生成Source Maps,选择合适的选项
devtool: "eval-source-map",
//唯一入口文件
entry: __dirname + "/app/main.js",
output: {
//打包后的文件存放的地方
path: __dirname + "/public",
//打包后输出文件的文件名
filename: "bundle.js"
},
devServer: {
//本地服务器所加载的页面所在的目录
contentBase: "./public",
//终端中输出结果为彩色
colors: true,
//不跳转
historyApiFallback: true,
//实时刷新
inline: true
}
}
Loaders
通过使用不同的loader,webpack通过调用外部的脚本或工具可以对各种各样的格式的文件进行处理,比如说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6,ES7)转换为现代浏览器可以识别的JS文件。
Loaders需要单独安装并且需要在webpack.config.js下的modules关键字下进行配置,Loaders的配置选项包括以下几方面:
|选项|描述|是否必须|
|test|一个匹配loaders所处理的文件的拓展名的正则表达式|是|
|loader|loader的名称|是|
|include/exclude|手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)|否|
|query|为loaders提供额外的设置选项|否|
我们把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,配置方法如下:
- 首先安装可以读取json文件的值的loader
npm install --save-dev json-loader
- 在webpack.config.js中进行配置json-loader:
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
}
]
},
}
- 创建带有问候信息的JSON文件(在app下创建config.json文件)
{
"greetText": "Hi there and greetings from JSON!"
}
- 更新app/Greeter.js的内容为:
//从config.json读取
var config = require('./config.json');
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = config.greetText;
return greet;
};
Babel
Babel其实是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:
- 下一代的JavaScript标准(ES6,ES7),这些标准目前并未被当前的浏览器完全的支持
- 使用基于JavaScript进行了拓展的语言,比如React的JSX
- 安装所有Babel所有的依赖包
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
- 在webpack.config.js中进行配置babel:
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
//在webpack的module部分的loaders里进行配置即可
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
}
]
},
}
现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。
现在使用React进行测试,先安装 React 和 React-DOM,在终端中输入
npm install --save react react-dom
- 更新app/Greeter.js的内容为:
//Greeter,js
//导入React
import React, {Component} from 'react'
//从config.json读取
import config from './config.json';
class Greeter extends Component{
render() {
return (
<div>
{config.greetText}
</div>
);
}
}
export default Greeter
- 更新app/main.js内容为:
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
render(<Greeter />, document.getElementById('root'));
Babel的配置选项
Babel可以在webpack.config.js中进行配置页可以分块单独配置,当配置较多时,最好单独配置创建一个.babelrc的babel配置文件,webpack会自动调用.babelrc其中的配置选项。
- 更新webpack.config.js内容为:
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
//在webpack的module部分的loaders里进行配置即可
loader: 'babel-loader',
}
]
},
}
- 创建.babelrc文件并添加内容如下:
{
"presets": ["react", "es2015"]
}
模块
Webpack有一个不可不说的优点,它把所有的文件都可以当做模块处理,包括你的JavaScript代码,也包括CSS和fonts以及图片等等等,只有通过合适的loaders,它们都可以被当做模块被处理。
CSS
webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。
- 安装css-loader 和 style-loader模块,在终端输入以下命令
npm install --save-dev style-loader css-loader
- 更新webpack.config.js内容,使其能够使用css-loader和 style-loader
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
//在webpack的module部分的loaders里进行配置即可
loader: 'babel-loader',
},
{
test: /\.css$/,
//添加对样式表的处理
loader: 'style-loader!css-loader'
}
]
},
}
>注:感叹号的作用在于使同一文件能够使用不同类型的loader
- 在app文件夹下创建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;
}
- 由于webpack只有单一的入口,其它的模块需要通过 import, require, url等导入相关位置,为了让webpack能找到”main.css“文件,我们把它导入”main.js “中,更新后的main.js文件内容为:
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
//使用require导入css文件
import './main.css';
render(<Greeter/>, document.getElementById('root'));
CSS module
CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递都所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中具有相同的类名可能会造成的问题.
- 更新webpack.config.js内容,使其能使用CSS module
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
//在webpack的module部分的loaders里进行配置即可
loader: 'babel-loader',
},
{
test: /\.css$/,
//添加对样式表的处理
//仅仅添加了?modules-loader
loader: 'style-loader!css-loader?modules-loader'
}
]
},
}
- 在app文件夹下创建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预处理器
Sass 和 Less之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句,
常用的CSS处理器loaders:
- Less Loader
- Sass Loader
- Stylus Loader
- 安装postcss-loader 和 autoprefixer(自动添加前缀的插件)
npm install --save-dev postcss-loader autoprefixer
- 更新webpack.config.js内容,使其能使用postcss-loader 和 autoprefixer,只需要新建一个postcss关键字,并在里面申明依赖的插件
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
//在webpack的module部分的loaders里进行配置即可
loader: 'babel-loader',
},
{
test: /\.css$/,
//添加对样式表的处理
//仅仅添加了?modules
loader: 'style-loader!css-loader?modules-loader!postcss-loader'
}
]
},
}
- 在webpack.config.js同级目录下创建postcss.config.js文件,配置内容如下;
module.exports = {
plugins: [
require('autoprefixer')
]
}
插件
插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
如何使用插件
要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)
- 添加一个显示版权声明的插件,在webpack.config.js中更新内容如下
var webpack = require('webpack');
module.exports = {
...
...
...
plugins: [
//在这个数组中new一个就可以了
new webpack.BannerPlugin("Copyright Flying Unicorns inc.")
],
}
HtmlWebpackPlugin
这个插件的作用是依据一个简单的模板,帮你生成最终的Html5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不同的哈希值。
这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些改变:
移除public文件夹,利用此插件,HTML5文件会自动生成,此外CSS已经通过前面的操作打包到JS中了,public文件夹里。
在app目录下,创建一个Html文件模板,这个模板包含title等其它你需要的元素,在编译过程中,本插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,在本例中我们命名模板文件名称为index.tmpl.html,模板源代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
</body>
</html>
- 更新webpack的配置文件,方法同上,新建一个build文件夹用来存放最终的输出文件
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
...
...
output: {
//打包后的文件存放的地方
path: __dirname + "/build",
//打包后输出文件的文件名
filename: "bundle.js"
},
...
...
...
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
})
],
}
优化插件
webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能
- OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
- UglifyJsPlugin:压缩JS代码;
- ExtractTextPlugin:分离CSS和JS文件
- 安装插件,在终端中输入
npm install --save-dev extract-text-webpack-plugin
- 在在webpack.config.js中更新配置内容如下
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
.
...
...
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("style.css")
})
],
}
注:
- 本文参考 http://www.jianshu.com/p/42e11515c10f,作为个人学习笔记
- 上述环境在ubuntu16.04 lts中测试成功
- 上述文字皆为个人看法,如有错误或建议请及时联系我