Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。 Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。
webpack是建立在node.js环境下的,所以要使用它我们需要先安装node和npm,其相关知识这里将不介绍。
在终端输入以下命令即可
$ sudo apt-get install nodejs
在终端输入以下命令即可
$ sudo apt-get install npm
为了保证下载速度,我们直接永久设置为淘宝源为npm的registry地址。在终端里输入
npm config set registry https://registry.npm.taobao.org
npm config get registry
sudo npm install -g webpack
npm install --save-dev webpack
输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。
npm install --save-dev webpack
<!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>
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
var greeter = require('./Greeter.js');
document.getElementById('root').appendChild(greeter());
webpack {entry file/入口文件} {destination for bundled file/存放bundle.js的地方}
只需要指定一个入口文件,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack没有进行全局安装,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中属于如下命令
node_modules/.bin/webpack app/main.js public/bundle.js
module.exports = {
//唯一入口文件
entry: __dirname + "/app/main.js",
output: {
//打包后的文件存放的地方
path: __dirname + "/public",
//打包后输出文件的文件名
filename: "bundle.js"
}
}
{
"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后命令行的输出显示
开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。
通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。
在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提供一个可选的本地开发服务器,这个本地服务器基于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
}
}
通过使用不同的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文件的值,配置方法如下:
npm install --save-dev json-loader
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
}
]
},
}
{
"greetText": "Hi there and greetings from JSON!"
}
//从config.json读取
var config = require('./config.json');
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = config.greetText;
return greet;
};
Babel其实是一个编译JavaScript的平台,它的强大之处表现在可以通过编译帮你达到以下目的:
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
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
//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
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
render(<Greeter />, document.getElementById('root'));
Babel可以在webpack.config.js中进行配置页可以分块单独配置,当配置较多时,最好单独配置创建一个.babelrc的babel配置文件,webpack会自动调用.babelrc其中的配置选项。
module.exports = {
...
...
...
//在配置文件里添加JSON loader
module: {
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
//在webpack的module部分的loaders里进行配置即可
loader: 'babel-loader',
}
]
},
}
{
"presets": ["react", "es2015"]
}
Webpack有一个不可不说的优点,它把所有的文件都可以当做模块处理,包括你的JavaScript代码,也包括CSS和fonts以及图片等等等,只有通过合适的loaders,它们都可以被当做模块被处理。
webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(…)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。
npm install --save-dev style-loader css-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'
}
]
},
}
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;
}
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
//使用require导入css文件
import './main.css';
render(<Greeter/>, document.getElementById('root'));
CSS modules 的技术就意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack从一开始就对CSS模块化提供了支持,在CSS loader中进行配置后,你所需要做的一切就是把”modules“传递都所需要的地方,然后就可以直接把CSS的类名传递到组件的代码中,且这样做只对当前组件有效,不必担心在不同的模块中具有相同的类名可能会造成的问题.
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'
}
]
},
}
.root {
background-color: #eee;
padding: 10px;
border: 3px solid #ccc;
}
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
Sass 和 Less之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句,
常用的CSS处理器loaders:
npm install --save-dev postcss-loader autoprefixer
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'
}
]
},
}
module.exports = {
plugins: [
require('autoprefixer')
]
}
插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)
var webpack = require('webpack');
module.exports = {
...
...
...
plugins: [
//在这个数组中new一个就可以了
new webpack.BannerPlugin("Copyright Flying Unicorns inc.")
],
}
这个插件的作用是依据一个简单的模板,帮你生成最终的Html5文件,这个文件中自动引用了你打包后的JS文件。每次编译都在文件名中插入一个不同的哈希值。
这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些改变:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
</body>
</html>
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安装,通过以下插件可以完成产品发布阶段所需的功能
npm install --save-dev extract-text-webpack-plugin
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")
})
],
}
注: