demo地址:https://github.com/yonglijia/...
首先来介绍下webpack是干嘛的。
webpack简单的来讲,是一个前端模块化管理和打包工具,可以将你的文件所依赖的js,css,node包等全部打包成一个bundle文件,并且能够处理各种模块之间依赖问题。在webpack的世界里,一切皆模块!但它又不仅仅是一个打包工具。它不仅能够轻松处理你项目中的依赖关系和加载顺序,还能让这个流程更加智能化,自动化。
webpack的使用,最复杂的一部分是莫过于它的配置项。webpack通过你的配置项,放置所有与打包相关的信息。一个基本的配置包括
module.exports = {
entry: '',
output: {},
module: {
rules: []
},
plugins: [],
};
我们可以这样理解这几个配置:
你如果要打包一个文件,那首先要指定文件的地址,也就是entry;打包之后放在那里呢,也就是output;打包过程中文件要经过怎么样的处理,也就是rules中的loader;如何能够使webpack打包更快,体积更小呢,也就是plugins。这些配置相辅相成,紧密结合
这些配置命名需要写入webpack.config.js中。在项目中,执行webpack
,就会自动引用这个文件。
下面我们详细介绍下这些配置。建立一个简单的项目,包含
testWebpck.js
module.exports = function() {
var testDiv = document.createElement('div');
testDiv.textContent = "hello world";
return testDiv;
};
index.js
var testWebpack = require('./testWebpack.js');
document.querySelector("#root").appendChild(testWebpack());
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>learn webpack</title>
</head>
<body>
<div id="root">
</div>
<script src="build/bundle.js"></script>
</body>
</html>
webpack.config.js
module.exports = {
entry: __dirname+"/app/index.js",//打包的js
output: {
path: __dirname + "/build",//打包后的文件存放的地方
filename: "bundle.js",//打包后bundle文件的文件名
},
module: {
rules: []
},
plugins: [],
};
我们在项目根目录中,执行webpack
,
浏览器打开index.html,可以看到hello world!
。修改testWebpck, 修改为“hello webpack!”,重新执行webpack,再次打开index.html,可以看到浏览器中变为hello webpack!
开发环境
我们可以使用webpack-dev-server,来构建一个本地服务器,通过这个服务器,监听你的代码,实时更新你修改的内容。
首先
npm install webpack-dev-server
然后在package.json中添加脚本
"scripts": {
"start": "webpack-dev-server --config webpack.config.js",
},
npm的
start
命令是一个特殊的脚本,直接使用npm start
就可以执行其对于的命令;而如果不是start
,想要在命令行中运行时,需要这样用npm run {script name}
如npm run build
执行npm start
,在浏览器中输入http://127.0.0.1:8080
,可以看到我们所看到的一模一样的页面。
Webpac-dev-server需要添加devServer配置
devServer{
hot:true,//
inline:true,//
port:8080,//默认8080
proxy:{//接口代理
'/xxx/**': {//接口匹配的地址
target:"代理地址",
secure: false
},
}
}
目前为止,我们已经使用wepack成功完成了一个文件的打包,并且能在开发环境中使用。但是未使用任何的loader,plugin,因为这里面涉及到的文件还太少,种类也不多。下面来一步步丰富这个小项目,引入所需要的配置。
我们在项目中引入css
index.css
#root{
border:1px solid red;
}
在index.js中引入require('./index.css');
执行npm start
,
这时已经报错,提示我们要引入相应的loader来处理css。是时候展示真正的技术了——— loader。
loader
loader是什么呢?正如我们上面所讲的是,它用来如何处理我们的打包文件的;现在如果不引入loader,那就无法处理css文件。loader不仅仅是处理css,还可以用来css的预处理、js的中使用ES6等高级语法处理成浏览器能兼容的格式。文件、图片、json等处理,都需要用到loader;经过loader的处理,这些文件能够很好的打入打包后的bundle中。
一个loader所需要的配置包括四个方面
test、loader、includer/exclude、query
首先来看个示范
{
test: /\.js$|\.jsx$/,
loader: 'babel-loader',
exclude: /(node_modules|bower_components)/,
query: {
presets: ['es2015', 'react'],
plugins: ['transform-class-properties', 'lodash']
},
},
test是一个正则表达式,它用来匹配适用于loader文件的扩展名
loader中是loader的名称,1.x版本不需要加上“-loader”,往后的版本需要加上“-loader”.
这两个都是必须添加的配置,下面两个是可选的。
include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)
query:为loaders提供额外的设置
CSS - loader
那我们来根据上面的配置来设置loader,来处理css。
webpack提供两个工具处理样式表,css-loader 和 style-loader;安装这两个npm包,修改我们的配置
module.exports = {
entry: __dirname+"/app/index.js",//打包的js
output: {
path: __dirname + "/build",//打包后的文件存放的地方
filename: "bundle.js",//打包后bundle文件的文件名
},
module: {
rules: [ {test: /\.css$/, loaders: ["style-loader", "css-loader"]},]//引入多个loader,loader要写成loaders,属性为一个数组,存放各个loader
},
plugins: [],
};
执行npm start
,在浏览器中我们发现,helllo world中已经添加上了红色的边框了。
css-loader使你能够使用类似@import和url(...)的方法实现require的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的js文件中
有一点要注意的是:loader是有顺序的。webpack肯定是先将所有css模块依赖解析完得到计算结果再创建style标签。因此应该把style-loader放在css-loader的前面(webpack loader的执行顺序是从右到左),顺序不对,会报错。
这样我们基本上使用了完成了一个loader的基本配置。
我们来配置下添加其他loader的配置项:
处理图片
需要同时安装url-loader和file-loader
{test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
url-loader与file-loader的工作方式相似,如果文件的体积比byte limit小,就能返回Data Url。如果图片比limit小,将直接以base64的形式内联在代码中。如果图片比limit(以bytes为单位)大,那么webpack就会使用file-loader去处理文件,并且所有的查询参数都会传递给file-loader。
我们想把js和图片打包成不同的文件夹,需要把output配置项修改一下
output: {
path: __dirname + "/build",//打包后的文件存放的地方
filename: "js/bundle.js",//打包后bundle文件的文件名
},
在项目app文件夹中分别添加两个图片,一个大于1024,一个小于1024,circle_loaction.png,data_update.gif
添加上面的配置,执行weibpack,发现一个有一个文件在build/images中生成。
这里面有一个路径的点需要注意:
如果在output中添加publicPath,比如说/xxx/
module.exports = {
entry: __dirname+"/app/index.js",//打包的js
output: {
publicPath: '/xxx/',
path: __dirname + "/build",//打包后的文件存放的地方
filename: "bundle.js",//打包后bundle文件的文件名
},
module: {
rules: [
{test: /\.css$/, loaders: ["style-loader", "css-loader"]},
{test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
]
},
plugins: [],
};
那我们访问http://127.0.0.1:8080
,发现bundle找不到。这是因为加上publicPath之后,访问内存中的文件bundle都需要加上/xxx/
(但实际上引用的是内存中的文件,既不是/build/js/也不是/xxx/)。所以要修改html中的引用地址方能使用。
<script src="xxx/js/bundle.js"></script>
publicPath表示资源的发布地址,加上该属性后,打包文件中所有通过相对路径引用的资源都会被配置的路径所替换。
如果此时修改index.css
#root{
border:1px solid red;
height: 350px;
background-image:url("./data_update.gif");
}
./data_update.gif 就会自动替换成xxx/images/data_update-37a1914078.gif。
path和publicPath的区别
path:/build/js
publicPaht:/online/
线下环境
path是打包后文件存放的路径,不能用于html中的js引用
publicPath表示的是资源发布的路径。自动指向path编译目录(/online/ => /build/js/),html中引用js文件时,必须引用此虚拟路径。
线上环境
webpack进行编译(当然是编译到/build/js/),我们需要把目录(/build/js/)下的文件,全部复制到/online/目录下(注意:不是去修改index.html中引用bundle.js的路径)
处理jsx文件
webpack不能直接处理jsx,需要借助于babel.
babel堪称神器,被誉为下一代 JavaScript 语法的编译器。用它,你可以不用等浏览器的支持,就可以使用最新的标准的语法。使用它可以解析jsx的语法。对于babel,不做过多介绍。
首先安装
npm install --save-dev babel-cli babel-preset-env babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom
创建 .babelrc
{
"presets": ["react","es2015"]
}
添加loader
{
test: /\.js$|\.jsx$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
}
修改index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>learn webpack</title>
</head>
<body>
<div id="root">
</div>
<div id="react">
</div>
<script src="build/js/index.js"></script>
<script src="build/js/testReact.js"></script>
</body>
</html>
添加testWebpack.jsx,
import React from 'react';
import ReactDOM from 'react-dom';
class TestReact extends React.Component{
constructor(props){
super(props);
}
render(){
return <div>this is a react div
<img src={require('./data_update.gif')}/>
</div>
}
}
ReactDOM.render(<TestReact/>,document.getElementById('react'))
修改配置
module.exports = {
entry: {
index :__dirname+"/app/index.js",
testReact:__dirname+"/app/testReact.jsx",
},
output: {
path: __dirname + "/build",//打包后的文件存放的地方
filename: "js/[name].js"//打包后输出文件的文件名
},
module: {
rules: [
{test: /\.css$/, loaders: ["style-loader", "css-loader"]},
{test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
{
test: /\.js$|\.jsx$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
}
]
},
plugins: [],
};
这里我们设置了两个入口文件,并且打包成不同的js,这个js的名字和他们本身的js的名字相同,通过filename: "js/[name].js"
指定。
这样设置完我们就完成了使用babel-loader处理jsx文件。
稍微展开一点entry:
entry 有三种类型字符串,数组和对象
entry:"xxx/index.js",
entry:["xxx/index.js","xxx/index2.js"],
entry:{
index:"xxx/index.js",
index2:"xxx/index2.js"
},
plugin
下面我们介绍下,如何添加插件,使我们的打包工程更快,更智能。
先介绍下一些常用的插件。
uglifyjs-webpack-plugin :JS压缩
var webpack = require('webpack');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: {
index :__dirname+"/app/index.js",
testReact:__dirname+"/app/testReact.jsx",
},
output: {
path: __dirname + "/build",//打包后的文件存放的地方
filename: "js/[name].js"//打包后输出文件的文件名
},
module: {
rules: [
{test: /\.css$/, loaders: ["style-loader", "css-loader"]},
{test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
{
test: /\.js$|\.jsx$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
}
]
},
plugins: [
new UglifyJSPlugin(),
],
};
我们这时候打开build/js中的js文件,发现里面都被压缩混淆了。
DefinePlugin:定义全局变量
-
Hot Module Replacement:
在webpack中实现HMR很简单,只需要两个配置
添加
new webpack.HotModuleReplacementPlugin()
修改脚本
"start": "webpack-dev-server --config webpack.config.js"
,
插件分为内置和外置的,不过用法都是大同小异的。不同的插件,配置参数也不一样。
其他配置
devtool:这个是用来配置soucemap的类型,在开发的时候调试特别有用。里面的配置特别多,后面会单独介绍这个地方。
-
resolve:配置短路径引用
resolve: { alias: { module: path.resolve(APP_PATH, 'module'), component: path.resolve(APP_PATH, "component"), service: path.resolve(APP_PATH, "service"), page: path.resolve(APP_PATH, "page"), node_modules: path.resolve(ROOT_PATH, 'node_modules') }, extensions: ['.js', '.jsx', '.json', '.scss'] },
使用这个选项的话,你可以直接使用比如
require('page/index')
,其实就是path.resolve(APP_PATH, "page")+'/index'
,extensions是用来匹配所要引用的文件类型,匹配以index开头的['.js', '.jsx', '.json', '.scss']
文件,如果没有,就会报错。
这篇文章权当个入门文档,里面还有很多需要一一深入挖掘的,比如devtool到底选择哪个最好,怎么使项目的打包更快,怎么使用dll等等,后续会一点一点的写。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。