webpack配置DEMO
webpack.config.js文件官方标配示例如下:
module.exports = {
dvtool: //配置生成Source Maps,选择合适的选项
plugins: //插件项
entry: //页面入口文件配置
output: //入口文件输出配置
path: //定义输出文件路径
filename: //指定打包文件名称
module: //加载器配置,对模块的处理逻辑
loaders: //定义了一些列的加载器
test: //正则,匹配到文件的后缀名
loader/loaders: //处理匹配到的文件----注意现在要求loader关键字不准省略"-loader"后缀
include: //包含的文件夹
exclude: //排除的文件夹
resolve: //其它解决方案的配置
extensions: //自动补全识别后缀
}
参考各路大神的流程写下来.....应该没有漏掉哪一步吧?
DEMO 项目结构如图:
index.html(加载打包后的build.js)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack test</title>
</head>
<body>
<div id="test"></div>
</body>
<!-- webpack一会打包完成的js -->
<script src="build.js"></script>
</html>
app.js(返回包含问候信息的html元素)
// app.js
module.exports = function() {
var hello = document.createElement('div');
hello.textContent = "Hello Everybody!";
return hello;
};
main.js(把app.js返回的节点插入页面)
//main.js
var hello = require('./app.js');
document.getElementById('test').appendChild(hello());
webpack可以在终端使用
webpack {entry file/入口文件} {destination for bundled file/存放build.js的地方}
非全局安装使用
node_modules/.bin/webpack app/main.js public/bundle.js
打开index.html
这里推荐通过配置文件来使用webpack
entry和output
在webpack.config.js中entry是唯一入口文件
首先entry的值可以有三种类型:1、字符串;2、数组;3、对象
//与DEMO无关,此为示例说明文件
//字符串,指定从这个文件路径下面的文件作为打包的入口文件
entry: './src/js/main.js' , //唯一入口文件
output: {
path: 'dist/js', //打包后的文件存放地方
filename: "build.js" //文件名
}
//存在多个入口时,可以使用array的方式。会将里面的文件一起打包到build.js
{
entry: ['./src/js/main.js', './src/js/test_entry.js'],
output: {
path: 'dist/js',
filename: "build.js"
}
}
//也可以是一个对象
{
entry: {
main: './src/js/main.js',
test_entry: './src/js/test_entry.js'
},
output: {
path: 'dist/js',
filename: '[name]-[chunkhash].js' //[name]的值是entry的键值,[hash]是打包时候的hash值,chunkhash是md5加密的值,这里作为版本号使用
}
}
每次修改文件,运行webpack后都会生成不一样的hash和chunkhash值,方便上线时候静态资源的版本管理。
因为文件名每次运行都是变化的,文件引入的文件名字也是需要变化的,这时候使用html-webpack-plugin插件 npm install html-webpack-plugin --save-dev
完成后在wenpack.config.js文件里添加plugins(plugins)的值是数组,里面的值都是new htmlWebpackPlugin(),参数见下文;
最后文件如下:
//与DEMO无关,此为示例说明文件
var htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
main: './src/js/main.js',
test_entry: './src/js/test_entry.js'
},
output: {
path: 'dist/js',
filename: '[name]-[chunkhash].js' //[name]的值是entry的键值,[hash]是打包时候的hash值,chunkhash是md5加密的值,这里作为版本号使用
},
plugins: [
new htmlWebpackPlugin({
title: 'webpack demo',
filename: 'index-[hash].html',
template: 'index.html'
})
]
}
- title: 用来生成页面的 title 元素
- filename: 输出的 HTML 文件名,默认是 index.html, 也可以直接配置带有子目录。
- template: 模板文件路径,支持加载器,比如 html!./index.html
- inject: true | 'head' | 'body' | false ,注入所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部,'head' 将放置到 head 元素中。
- favicon: 添加特定的 favicon 路径到输出的 HTML 文件中。
- minify: {} | false , 传递 html-minifier 选项给 minify 输出
- hash: true | false, 如果为 true, 将添加一个唯一的 webpack 编译 hash 到所有包含的脚本和 CSS 文件,对于解除 cache 很有用。
- cache: true | false,如果为 true, 这是默认值,仅仅在文件修改之后才会发布文件。
- showErrors: true | false, 如果为 true, 这是默认值,错误信息会写入到 HTML 页面中
- chunks: 允许只添加某些块 (比如,仅仅 unit test 块)
- chunksSortMode: 允许控制块在添加到页面之前的排序方式,支持的值:'none' | 'default' | {function}-default:'auto'
- excludeChunks: 允许跳过某些块,(比如,跳过单元测试的块)
在当前demo根目录下新建一个webpack.config.js的文件,首先最简单的写一下入口文件和存放打包后文件的地方的路径
//DEMO
module.exports = {
entry: __dirname + "/app/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放地方
filename: "build.js" //打包后输出的文件的文件名
}
}
_dirname 是node.js的一个全局变量,指向当前执行脚本所在的目录
在终端里运行webpack node_modules/.bin/webpack
(非全局安装)
那么在非全局安装webpack中,或者是类似于上面命令很长很容易出错的时候,我们可以用npm引导任务执行,在package.json中对npm脚本部分进行设置,可以使用简单的npm start
命令来代替:
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack" //相当于把npm的start命令指向webpack命令
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.3.3"
}
}
这时候的编译后是虚拟的,本地其实是没有编译的,所以很多使用本地路径的都需要我们去处理。
无论全局还是局部安装的webpack,这里都不需要写前面详细路径,因为package.json脚本部分已经默认在命令中添加了node_module/.bin路径。
devtool
生成Source Maps可以使开发更容易。打包后的文件有时候是不容易找到错的地方对应的源码的。通过简单的配置,webpack在打包时候会为我们生成source maps,这可以为我们提供一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,更容易调试。
devtool选项 | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度; |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也是的浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便; |
eval-source-map | 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的source map,但是对打包后输出的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。这个选项可以在不影响构建速度的前提下生成完整的source map,但是对打包后输出的js文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项; |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map的方法,生成的source map会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点 |
接下来生成Source Maps这可以使调试更加的容易,因为这只是一个测试的小项目,所以推荐使用eval-source-map
,不过只是在开发阶段使用。
//DEMO
module.exports = {
devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
entry: __dirname + "/app/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放地方
filename: "build.js" //打包后输出的文件的文件名
}
}
使用webpack构建本地服务器
webpack可以提供一个可选的本地开发服务器,该服务器是基于node.js构建,不过它是一个单独的组件(webpack-dev-server是一个独立的NPM包),在webpack中进行配置之前需要单独安装它作为项目依赖。这一步搞好了,嗯哼~你的浏览器就可以监测你代码的修改然后自动刷新了。
npm install --save-dev webpack-dev-server
接下来我们调整一下配置文件:
module.exports = {
devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
entry: __dirname + "/app/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放地方
publicPath:"/assets/", //运行后你看不到,因为实时编译是保存到了内存中
filename: "build.js" //打包后输出的文件的文件名
}
}
调整一下index.html的js引用路径 <script src="assets/build.js"></script>
运行webpack-dev-server --content-base ./public
得到如下(这里如果不进行设定的话,默认是当前目录下的):
打开浏览器输入:localhost:8080 你就能看到你的hello信息了....
对上面设定的content-base做一些说明:
注意:如果在webpack.config.js里面如果配置了output的publicPath这个字段的值的话,在index.html文件里面也应该做出调整,因此才需要调整index.html。上述配置了这个字段是为了对实时编译保存到内存中做一个说明,你也可以省略这个字段,那么index.html的js引用路径就依然是<script src="build.js"></script>
有同学要说,坑爹啊,我想要的是实时编译,实时刷新,浏览器自刷新。你这个是手动的什么鬼??
webpack-dev-server目前支持两种自动刷新的方式:
- iframe mode
- inline mode
这2种模式的配置方式和访问路径稍微有点区别,最主要的区别是:Iframe mode是在网页中嵌入了一个iframe,将我们自己的应用注入到这个iframe中,因此你每次修改的文件都是对这个iframe进行了reload。而inline mode是webpack-dev-server会在wenpack.config.js的入口配置文件中再添加一个入口。
module.exports = {
devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
entry: {
app: [
'webpack-dev-derver/client?http://localhost:8080/',
__dirname + "/app/main.js", //唯一入口文件
]
},
output: {
path: __dirname + "/public", //打包后的文件存放地方
filename: "build.js" //打包后输出的文件的文件名
}
}
你也可以直接在index.html中引入这部分代码:<script src="http://localhost:8080/webpack-dev-server.js"></script>
Iframe mode
浏览器访问路径变为:localhost:8080/webpack-dev-server/index.html
页面的header部分会出现整个reload消息的状态,当改变源文件时候,就可以自动完成编译打包,页面自动刷新。
Inline code
使用时候,cmd line需要写成webpack-dev-server --inline --content-base ./public
浏览器访问路径是:localhost:8080/index.html
它会在控制台中显示reload状态,也具有自动编译打包的功能。
hot module replacement
开启hot module replacement 功能,在cmd line里面添加 --hot就可以
webpack-dev-derver --hot --inline --content-base ./public
其它的配置项比如:
- quiet 控制台中不输出打包信息
- compress 开启gzip压缩
- propress 显示打包的进度
调整package.json里面的配置项
"scripts": {
"dev": "webpack-dev-server --devtool eval-source-map --progress --colors --hot --inline --content-base ./public",
"build": "webpack --progress --colors"
}
接下来就可以使用npm run dev
在浏览器中打开localhost:8080/index.html 进行愉快开发了
备注一下devserver配置选项
devserver配置选项 | 描述 |
---|---|
contentBase | 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在的目录(比如DEMO中的“public”目录) |
port | 设置默认监听端口,默认为“8080” |
inline | 设置为true,当源文件改变时,会自动刷新页面 |
colors | 设置为true,终端输出的文件为彩色的 |
historyApiFallback | 开发单页面应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html |
module-loaders
loaders中对我来说最难的是babel,我看着大神的教程N久没折腾出来,就为了体验把ES6,也是拼了。
按照大神的教程,继续....希望这次折腾成功 come on baby
首先明确大名鼎鼎的loaders在webpack中为啥是个很犀利的功能。主要就是通过使用不同的loader,webpack可以通过调用外部的脚本或工具,对各种格式的文件进行处理。比如分析JSON文件把它转换为JS文件,或者是ES6/ES7转换为现代浏览器可以识别的JS文件。还可以将react的jsx文件转换成JS文件。厉害了我的loader....
loaders需要单独安装
npm install --save-dev json-loader
安装成功后需要在webpack.config.js下的module关键字下进行配置,配置项如下:
- test: 一个匹配loaders处理的文件的拓展名的正则表达式(必写)
- loader: loader的名称(必写)
- include/exclude: 手动添加必须处理的文件(文件夹)/屏蔽不需要处理的文件(文件夹)(非必写)
- query: 为loaders提供额外的设置选项(非必写)
webpack.config.js
module.exports = {
devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
entry: {
app: [
__dirname + "/app/main.js", //唯一入口文件
]
},
output: {
path: __dirname + "/public", //打包后的文件存放地方
filename: "build.js" //打包后输出的文件的文件名
},
module: { //配置文件里添加json loader
loaders: [
{
test: /\.json$/,
loader: "json-loader"
}
]
},
}
DEMO继续,把app.js的message单独放在一个message.json文件里,再进行配置,让app.js可以读取到json中的message。
message.json
{
"hello": "Hello everybody and from json file!"
}
app.js
// app.js
var message = require('./message.json');
module.exports = function() {
var hello = document.createElement('div');
hello.textContent = message.hello;
return hello;
};
嗯哼,走你~~
这个是简单的了。接下来是我有爱有恨的babel
Babel其实是一个编译JavaScript的平台,强大之处就是:ES6/ES7转换为现代浏览器可以识别的JS文件。还可以将react的jsx文件转换成JS文件,简直占据了目前我的loader的半壁江山啊。
Babel其实就是几个模块化的包,核心功能位于babel-core
的npm包中,不过webpack把它们整合在一起使用了,但是对于每一个你需要的功能或者拓展,你都需要安装单独的包(比如解析Es6的babel-preset-es2015和解析JSX的babel-preset-react包)
我们来个全家桶一次搞定:
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
(npm一次性安装多个依赖模块,模块之间用空格隔开)
配置webpack.config.js:
module.exports = {
devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
entry: {
app: [
__dirname + "/app/main.js", //唯一入口文件
]
},
output: {
path: __dirname + "/public", //打包后的文件存放地方
filename: "build.js" //打包后输出的文件的文件名
},
module: { //配置文件里添加json loader
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
}
]
}
}
因为demo会用的react所以记得安装react和react-dom
npm install --save react react-dom
在app.js里使用es6新语法开始愉快的返回一个react组件试一下吧
import React, {Component} from 'react'
import message from './message.json'
class Hello extends Component {
render() {
return (
<div>
{message.hello}
</div>
);
}
}
export default Hello
在main.js使用ES6的模块定义和渲染Hello模块
import React from 'react';
import {render} from 'react-dom';
import Hello from './app.js';
render(<Hello />, document.getElementById("test"));
考虑到babel有非常多的配置选项,在webpack.config.js里面进行配置的话会显得太复杂,因此比较好的方式是把babel的配置选项单独放在一个.babelrc
的配置文件中。webpack会自动调用这个配置文件的选项。因此我们将刚才的配置分离出来到.babelrc:
{
"presets": ["react", "es2015"]
}
穿插点题外的。webpack对于模块有非常强大的处理功能,那么什么算是模块呢??
事实上一切皆模块,包括我们的css、fonts、图片、js等,当通过合适的loaders时,它们都可以被当成模块来处理。
CSS
webpack提供了两个工具处理样式表,css-loader和style-loader,这两个处理的任务不同。css-loader使你能够以类似@import 或者 url(...) 这种方式实现 require()的功能,style-loader是将所有计算后的样式加入页面中,两者组合在一起就能使样式表嵌入webpack打包后的js文件中。
DEMO继续:
npm install --save-dev style-loader css-loader
webpack.config.js中添加配置:
{
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, Arial, sans-serif;
background: #f2f2f2;
}
h1, h2, h3, h4, h5, h6, p, ul {
margin: 0;
padding: 0;
}
webpack只有单一入口,其它的模块需要通过import、require、url等导入相关位置,现在将main.css导入main.js中
import React from 'react';
import {render} from 'react-dom';
import Hello from './app.js';
import './main.css';
render(<Hello />, document.getElementById("test"));
通常情况下,css会和js打包到同一个文件中,并不会打包一个单独的css文件,通过合适的配置webpack的话也可以把css打包为单独的文件的。
CSS module
随着前端模块化开发的推动,css module出现了。它将js的模块化思想带入css中来,通过css模块,所有的类名、动画名默认都只作用于当前模块。webpack一开始就对css模块化提供了支持,在css loader中进行配置后,你所需要做的就是把modules传递到需要的地方,然后就可以直接把css的类名传递到组件的代码中,且只对当前组件有效,不比担心在不同的模块中具有相同的类名可能会造成的问题,实现方式先调整webpack.config.js中的css配置
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules' //跟前面相比就在后面加上了?modules
}
创建一个app.js文件
#test {
background-color: pink;
padding: 10px;
border: 5px solid green;
}
导入#test 到app.js中
import React, {Component} from 'react';
import message from './message.json';
import styles from './app.css';
class Hello extends Component {
render() {
return (
<div className={styles.test}>
{message.hello}
</div>
);
}
}
export default Hello
相同的类名也不会造成不同组件之间的污染..
css modules是一个很大的主题,可以参考cssModules官方文档查看更多
css预处理器
Sass和Less之类的预处理器是对原生css的拓展,它们允许你使用类似于variables,nesting,mixins,inheritance等不存在于css中的特性来写css,css预处理器可以使这些特殊的语句转化为浏览器可以识别的css语句。
常用的loaders:
- Less Loader
- Sass Loader
- Stylus Loader
还有一个css的处理平台-PostCss,具体可以参考PostCss官方文档
安装postcss和autoprefixer(自动添加前缀的插件)可以参考autoprefixer官方文档
npm install --save-dev postcss-loader autoprefixer
新建一个postcss.config.js文件,并在里面申明依赖的插件
module.exports = {
plugins: [
require ('autoprefixer')
]
}
webpack.config.js 如下现在你写的css会自动根据can i use 里的数据添加不同的前缀了。
module.exports = {
devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
entry: {
app: [
__dirname + "/app/main.js", //唯一入口文件
]
},
output: {
path: __dirname + "/public", //打包后的文件存放地方
filename: "build.js" //打包后输出的文件的文件名
},
module: { //配置文件里添加json loader
loaders: [
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules!postcss-loader'
}
]
}
}
插件(plugins)
loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less...),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。
webpack分为内置插件和第三方插件。在笔记二中对这两者有基础介绍。下一节笔记就正式开始参考官方进行vue的渐进学习。
更多文章请移步 http://www.yuki.kim