侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

webpack从零开始

2024-05-12 星期日 / 0 评论 / 0 点赞 / 94 阅读 / 23218 字

webpack是一个基于node.js编写的资源整合打包器(官方原称:MODULE BUNDLER),通过指定入口文件,他能将该入口文件中引用的所有前端资源都合并打包,并最终输出到你指定的输出目录。w

webpack是一个基于node.js编写的资源整合打包器(官方原称:MODULE BUNDLER),通过指定入口文件,他能将该入口文件中引用的所有前端资源都合并打包,并最终输出到你指定的输出目录。webpack进阶比gulp要复杂得多,本文只以从零开始上手使用为指导。

本文所示代码,可通过git@osc的这个项目获取到:http://git.oschina.net/janpoem/webpack-tutorial。

本文中可能在一些细节上表述得不够清晰,完全按照文章的顺序去阅读和执行,可能会碰到一些问题(也是webpack有太多的细节),所以建议先获取到源代码,先执行个npm install,让他慢慢跑,然后一边看, 一边参考着代码来阅读,能更好的帮助理解文章说到的一些东西。

并且如果有碰到本文中执行不下去,出问题的,欢迎提出严厉的批评和指责!

简单的开始

从零开始

以下部分如果你已经了解node.js和npm,请忽略跳过,直接进入《webpack安装》。

首先,你要确保你的电脑安装了node.js,点击这里找到适合你系统的版本下载并安装。并确保你的系统内能正确执行以下命令:

npm -vnode -v

node.js是什么?

Node.js是一个Javascript运行环境(runtime)。实际上它是对Google V8引擎进行了封装。V8引 擎执行Javascript的速度非常快,性能非常好。Node.js对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。

npm是什么?

NPM的全称是Node Package Manager ,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。

webpack安装

如果你对webpack的基本安装和使用已经十分了解,并已经安装,可以跳过本章节,并直接进入《webpack进阶》章节。

接下来你需要在全局环境内安装webpack和webpack-dev-server,当然后者并不是必须的,但还是强烈推荐你安装。

npm install webpack webpack-dev-server -g

初次使用

随便建立一个测试的目录,比如:webpack-first,用命令行进入,或者webstorm/phpstorm/atom等打开这个目录。

在目录中添加一个文件:webpack.config.js

module.exports = {	entry: [		"./main.js"	],	output: {		path: './output',		filename: 'app.js'	}};

在这个文件中,我们声明了一个入口文件,为当前目录下的main.js,并且输出目录的基础路径为当前目录下的output,输出的文件名为app.js

接着,我们往下添加test.js和main.js文件,main.js如上所述,为项目的入口文件,test.js为需要引入在main.js中的模块。

test.js

module.exports = [	'a', 'b', 'c'];

main.js

// 引入test.js文件,并将其输出的内容(module.exports)赋值到test变量上var test = require('./test');// 在浏览器的console中输出test变量的内容console.log(test);

进入命令行模式(如果是phpstorm/webstorm可以打开他的Terminal工具),输入指令:webpack,会看到他执行的结果:

该命令执行完,webpack会在你的项目内添加一个output的目录,打开这个目录,你会看到根据你的webpack.config.js配置,他生成了一个app.js文件。

这里特别说明一下,如果你仅仅只是需要用webpack来打包最原始的js文件,是不需要在这个项目内安装webpack和webpack-dev-server的,他会使用你全局(就是刚才npm install -g的)安装的版本,这点比gulp好多了。

使用webpack-dev-server

webpack-dev-server,是webpack提供的一个插件,他提供了一个http服务器环境,给你实时预览打包合并的结果。

特别提前说明的是,使用webpack-dev-server指令,你必须指定--content-base指令,--content-base指令,用于指定http服务器的document root目录。

在刚才的项目根目录中,执行以下命令:webpack-dev-server --content-base ./public,我们以当前目录下的public目录作为http服务器的根目录:

特别说明:在webpack.config.js中指定的output目录和--content-base并没有必然的关系,output指定的是你输出的路径。

在项目中添加public目录,并添加一个用于测试的文件:public/index.html

<!DOCTYPE html><html lang="en"><head>	<meta charset="UTF-8">	<title>webpack first sample</title></head><body><!--引入output中的app.js文件--><script type="text/javascript" src="app.js"></script></body></html>

访问http://localhost:8080/,并打开你的浏览器调试工具,你将能看到在main.js中输出的信息。

除此之外,你还可以访问:http://localhost:8080/webpack-dev-server/,这里提供了一个基于iframe的方式加载index.html的页面,并且当你每次修改完js文件时,他都会自动刷新这个页面以实时预览。

webpack进阶

上述可以作为一个基本的上手说明,这里要说的,就是一些比较实际——和项目实际关联的内容。

要使用进阶功能,就需要在项目内安装webpack和webpack-dev-server了,如果你还没执行npm init,可以先执行一次npm init,他会需要你回答一些问题,并生成一个与当前项目相关的package.json文件。package.json文件,能有效的管理你的node.js项目的版本,已经依赖库,执行脚本,等信息,尤其当你将项目提交到代码仓库,别人也需要执行你的项目时,通过package.json文件,能够很好的克服不同的系统环境下,缺失的依赖类库。

npm init执行以后,请执行下面的命令:

npm install webpack webpack-dev-server --save-dev

这个指令的含义是,在当前项目内安装最新版本的webpack和webpack-dev-server。

--save-dev参数,用于告诉npm这两个类库的信息需要写入到package.json中,并作为开发依赖库。npm还有一个--save指令,与--save-dev类似,但他关联的是直接依赖。--save-dev和--save,决定了别人在引用你的项目时,你项目依赖库是否也安装在对方的node_modules目录中,这个详细就不在这里展开了。

执行完这两个命令,打开你的package.json文件,你会看到如下内容:

npm命令需要访问国外的npm库,如果你感觉你当前的网络环境出国外不是那么稳定,可以考虑使用cnpm,点击这里看教程,这里就不再介绍了。

webpack-dev-server命令行参数

在webpack-dev-server包含了一些比较常用的参数,可以大大提高我们的开发效率,这里介绍几个最常用的:

--port <端口号>: 指定http服务的端口号

--host <主机名>: 指定http服务的主机名,这在局域网内使用实时调试非常有用。

--compress: 启用gzip压缩

--inline: 将webpack-dev-server的运行时文件合并打包到输出的文件中

--hot: 使用HotModuleReplacementPlugin插件(已经整合在webpack中,无需npm安装),并将http服务器切换到hot模式,其实所谓hot模式,就是当你更改了某个被引用的文件,会hot replace,并重新合并到输出文件中。

一般来说,--hot --inline会合并使用,这个方式会合并将webpack/hot/dev-server打包到输出文件中。这个webpack/hot/dev-server,实际上就是这个http服务器的hot replace的核心逻辑,而这个东西,其实在你就是你当前项目的node_modules/webpack/hot/dev-server.js文件。打开node_modules/webpack/hot/目录,实际上你会看到这里还有其他的几种模式,比如only-dev-server等。事实上你是可以显式的将这些文件配置在你的webpack.config.js文件中里的,比如:

module.exports = {	entry: [		"webpack/hot/only-dev-server",		"./main.js"	],	output: {		path: './output',		filename: 'app.js'	}};

当你指定了only-dev-server以后,执行:webpack-dev-server --content-base ./public --hot时,注意输出的变化:

而如果我把webpack.config.js中的webpack/hot/only-dev-server注释掉,再执行:webpack-dev-server --content-base ./public --hot --inline

这是官方文档非常隐晦的地方,没有明确说明的地方。

时空门:webpack-dev-server官方说明。

而webpack本身比较常用的参数有:

--devtool : 调试工具的模式,eval是将你的css和js代码变为eval的方式合并打包。

--config : 指定配置文件

--progress: 在命令行终端输出编译合并的过程信息

--colors: 在命令行终端中显示带颜色的信息

更多更详细的信息,可以看这里,webpack CLI模式的说明。

webpack还提供了一个通过你自己书写代码来启动http服务的功能,这个应该说是最高阶的内容了,本文就不讨论了,有兴趣的可以去官方文档看:webpack-dev-server,这篇文档的下半部分,就是完全由你自己实现一个http服务的简单教程。详细的可以去看webpack node.js API。

除此之外,这篇文章是你使用webpack以前,必须阅读的:webpack config配置参数说明,每个项目有自身的特殊性,本文只是希望能将一些基本常用的东西能做一个涵盖,所以完全靠本文的内容,肯定无法解决每个项目实际碰到的问题,所以熟读这个配置文档,应该是最起码的要求。

webpack loaders

在实用层面的第二个问题就是各种loaders,这里分两个部分,一个是js部分,包括如js近亲系的es6、jsx、babel tranplier、coffee等,第二部分为CSS以及其近亲,如less、stylus,同时包括其他前端的静态资源的引用问题。

这里是webpack loaders的清单。

加载JS部分

基于webpack打包,你可以比以往过去更加放心大胆的使用各种奇葩,前所未见的各种脚本语言——当然这里的前提是你能自己处理好他们的转译问题。而目前babel已经提供了大多数常见的语言、css、html模板的loaders,不过根据我实际经验,webpack主要的优势还是在于处理js的合并打包。

以下就以es6和jsx为例(使用babel-loader):

npm install babel-loader babel-preset-es2015 babel-preset-react --save-dev

如果你需要使用到babel的一些插件,也需要通过npm来进行安装,关于babel如何使用的问题,可以参考我写的这篇文章:《Babel指南 - 基本环境搭建》。

npm install babel-plugin-transform-class-properties babel-plugin-transform-es2015-block-scoping babel-plugin-transform-es2015-computed-properties --save-dev

在webpack.config.js中,我们修改如下配置:

module.exports = {	entry: [		"./main.js"	],	output: {		path: './output',		filename: 'app.js'	},	module: {		loaders: [			{				test: //.(es6|jsx)?$/,				exclude: /(node_modules|bower_components)/,				loader: 'babel', // 'babel-loader' is also a legal name to reference				query: {					presets: ['es2015', 'react'],					plugins: [						"transform-es2015-block-scoping",						"transform-class-properties",						"transform-es2015-computed-properties"					]				}			}		]	}};

其实也很简单,通过这样,你就可以随意的使用es6和jsx了,比如我们添加一个例子hello_world.jsx:

注意,要使用到react,你还是需要先安装:

npm install react react-dom --save-dev

hello_world.jsx代码如下

var React = require('react');class HelloWorld extends React.Component {	constructor(props) {		super(props);		this.state = {			url: '',			value: 'http://tool.oschina.net/'		};	}	getUrl() {		let url = this.state.value;		if (/^/////.test(url)) {			url = 'http:' + url;		}		else if (!/^https?/://///i.test(url)) {			url = 'http://' + url;		}		return url;	}	changeUrl(value) {		this.setState({value: value});	}	goUrl(value, event) {		if (event && event.preventDefault)			event.preventDefault();		this.setState({url: value});	}	renderFrame() {		if (this.state.url) {			return		}	}	componentDidMount() {		this.goUrl(this.getUrl());	}	render() {		return <div>			<h1>Hello world!!</h1>			<form onSubmit={(e) => this.goUrl(this.getUrl(), e)}>				<input type="text" value={this.state.value} onChange={(e) => this.changeUrl(e.target.value)}/>				<button>Hello world</button>			</form>			{this.renderFrame()}		</div>;	}}module.exports = HelloWorld;

接着修改main.js:

var test = require('./test');var ReactDOM = require('react-dom');var React = require('react');var HelloWorld = require('./hello_world.jsx');var doc = document, body = doc.body;body.onload = function() {	var el = doc.createElement('div');	el.style.opacity = 0;	el.style.marginTop = '-100px';	el.style.transitionProperty = 'opacity margin-top';	el.style.transitionDuration = '800ms';	el.style.transitionTimingFunction = 'cubic-bezier(0.65,-0.1, 0.24, 1.47)';	body.appendChild(el);	ReactDOM.render(React.createElement(HelloWorld), el);	setTimeout(function() {		el.style.opacity = 1;		el.style.marginTop = 0;	}, 1);};

访问http://localhost:8080/就会看到:

加载CSS部分

要在webpack中引入css文件,需要简单的做一个梳理。

JS中引入CSS文件

通过css-loader,是可以将一个css文件用require函数,在代码中被引用,并且返回这个css中样式定义的文字内容。当然首先你必须安装css-loader:

npm install css-loader --save-dev

然后我们就可以在js中引入css

var css = require('css!./style.css')

但这样,只是将css的内容引入,并没有加载到页面上,所以我们需要修改main.js:

var test = require('./test');var ReactDOM = require('react-dom');var React = require('react');var HelloWorld = require('./hello_world.jsx');var css = require('css!./style.css');var doc = document, body = doc.body;body.onload = function() {	// 在head中把样式加载	var style = doc.createElement('style');	style.innerText = css.toString();	doc.head.appendChild(style);	var el = doc.createElement('div');	el.style.opacity = 0;	el.style.marginTop = '-100px';	el.style.transitionProperty = 'opacity margin-top';	el.style.transitionDuration = '800ms';	el.style.transitionTimingFunction = 'cubic-bezier(0.65,-0.1, 0.24, 1.47)';	body.appendChild(el);	ReactDOM.render(React.createElement(HelloWorld), el);	setTimeout(function() {		el.style.opacity = 1;		el.style.marginTop = 0;	}, 1);};

这样,我们会看到http://localhost:8080/已经加载到style.css的样式了。关于css-loader的详细用法,可以去css-loader的github看看。

注意,目前我们还没去修改webpack.config.js。

style-loader

style-loader:可以将一个已经输出的内容变为一个style的DOM标签输出。安装:

npm install style-loader --save-dev

这时候去掉刚才在body.onload中增加的在head加入输出css的代码,修改main.js如下:

var test = require('./test');var ReactDOM = require('react-dom');var React = require('react');var HelloWorld = require('./hello_world.jsx');var css = require('style!css!./style.css');var doc = document, body = doc.body;body.onload = function() {	var el = doc.createElement('div');	el.style.opacity = 0;	el.style.marginTop = '-100px';	el.style.transitionProperty = 'opacity margin-top';	el.style.transitionDuration = '800ms';	el.style.transitionTimingFunction = 'cubic-bezier(0.65,-0.1, 0.24, 1.47)';	body.appendChild(el);	ReactDOM.render(React.createElement(HelloWorld), el);	setTimeout(function() {		el.style.opacity = 1;		el.style.marginTop = 0;	}, 1);};

注意:style!css!./style.css。这时候得到的效果,和刚才是一样的。

为什么要不厌其烦的将css和style的loader拆开两个章节来介绍呢,因为所有less、stylus实际上都是最终都是基于这个机制在运作的。

加载图片、字体、svg等资源

如果需要在样式中加载图片,那么就需要url-loader,而如字体,则需要使用file-loader,还是安装:

npm install url-loader file-loader --save-dev

注意,这里就是webpack和gulp最大的区别,webpack中,只要在你的源码中存在被引用的资源,你都需要说明这些资源需要被如何加载。事实上,webpack还有如base64-loader,你也完全可以将一个图片作为base64-loader来处理。当然这里我们就以url-loader处理。

使用不同的loader,将决定了合并打包后的处理方式,如果使用base64-loader,他当然会将图片的内容打包成base64编码合并在js中。而url-loader,则会在输出的目录生成对应的文件(只有本地文件,会输出到output目录下)。

修改webpack.config.js:

module.exports = {	entry: [		"./main.js"	],	output: {		path: './output',		filename: 'app.js'	},	module: {		loaders: [			{				test: //.(es6|jsx)?$/,				exclude: /(node_modules|bower_components)/,				loader: 'babel', // 'babel-loader' is also a legal name to reference				query: {					presets: ['es2015', 'react'],					plugins: [						"transform-es2015-block-scoping",						"transform-class-properties",						"transform-es2015-computed-properties"					]				}			},			{				test: //.(png|jpg|jpeg|gif|(woff|woff2)?(/?v=[0-9]/.[0-9]/.[0-9])?)$/,				loader: 'url-loader?limit=1000'			},			{				test: //.(ttf|eot|svg)(/?[/s/S]+)?$/,				loader: 'file'			}		]	}};

然后重启webpack-dev-server,你就能看到图片、svg等资源已经能正确的加载。

关于合并打包,我们最后会说到。

加载CSS近亲

这里以stylus为例,其他less、scss等的也都是同理的。

npm install stylus stylus-loader --save-dev

同样的,你只要使用require即可在页面引入styl文件:

require('style!css!stylus!./hello.styl');

如果你觉得使用style!css!stylus!的方式加载文件,过于怪异,那么你可以修改webpack.config.js文件,增加两个loader:

{	test: //.styl$/,	loader: "style!css!stylus"},{	test: //.css$/,	loader: "style!css"}

这样,你就可以使用顺眼一点的方式来加载:

require('./style.css');require('./hello.styl');

最终打包

到此,我们已经完成了全部的编码。接下来就要将内容打包输出了,这时候你只要在项目的根目录下执行一下webpack即可。

这时候我们会看到,在output目录下,他输出了一个app.js和svg文件。而本例子中的两个css文件,他全部合并到打包到了app.js文件中。

如果你希望将css文件从js文件中分离出来,需要一个额外的插件:

npm install extract-text-webpack-plugin --save-dev

这个插件可以指定拦截特定加载器输出的文本内容,并最终合并输出到你指定的文件上去,但要特别注意,使用这个插件后,webpack-dev-server自动输出样式调试就会因为这个插件而失效,所以建议构建的时候,使用单独的配置文件,比如添加一个webpack.build.js,使用指令:

webpack --config webpack.build.js

webpack.build.js内容如下:

const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = {	entry: [		"./main.js"	],	output: {		path: './output',		filename: 'app.js'	},	module: {		loaders: [			{				test: //.(es6|jsx)?$/,				exclude: /(node_modules|bower_components)/,				loader: 'babel',				query: {					presets: ['es2015', 'react'],					plugins: [						"transform-es2015-block-scoping",						"transform-class-properties",						"transform-es2015-computed-properties"					]				}			},			{				test: //.(png|jpg|jpeg|gif|(woff|woff2)?(/?v=[0-9]/.[0-9]/.[0-9])?)$/,				loader: 'url-loader?limit=1000'			},			{				test: //.(ttf|eot|svg)(/?[/s/S]+)?$/,				loader: 'file'			},			{				test: //.styl$/,				// loader: "style!css!stylus"				loader: ExtractTextPlugin.extract('style', 'css!stylus')			},			{				test: //.css$/,				// loader: "style!css"				loader: ExtractTextPlugin.extract('style', 'css')			}		]	},	plugins: [		new ExtractTextPlugin('app.css')	]};

这次就能将css文件分离出来了。

webpack还有很多可调节、可优化的配置,但是碍于篇幅,在这里就不再详细展开了。可能有一些细节的地方,本文没有介绍的很清楚,也请多多见谅。

本文所示代码,可通过git@osc的这个项目获取到:http://git.oschina.net/janpoem/webpack-tutorial。

最后补一张示例代码的最终的效果图:

 

广告 广告

评论区