webpack入门级篇章,看这篇就够了
序言
最近由于要准备关于前端的分享课程,不知道要讲些什么,什么最重要,回想了一下,感觉什么都比较重要(哈哈哈),思来想去还是觉得讲一下《webpack自动化打包》,为什么呢,因为webpack如日中天,哈哈,不扯淡了,下面进入正题。
Webpack到底是什么鬼
官方的概念:webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。通俗点理解就是你前端的东西,然后通过它将其打包为合适的格式以供浏览器使用,它火起来的原因是单页面的应用和js模块化,webpack提供了扩展的机制,在很多社区的支持下,在各个方面都得到了拓展和应用。
Webpack和Gulp、Grunt有什么区别,为什么使用Webpack
读了上面的,可能会有人问,不是有grunt和gulp的打包工具吗,他们也很好用啊,我觉得很好啊,其实webpack和另外两个没有可比性,不同之处:
gulp和grunt是一种能够优化前端的开发流程的工具,而webpack是一种模块化的解决方案。
gulp和grunt工作方式是在一个配置文件中,指明对某一些文件进行编译、组合、压缩等任务的具体步骤,然后它就可以帮助你去完成任务了,最大的问题就是不能不能按需打包,更别说按需加载,如果程序中用不到的文件,也会被打包进来;Webpack的工作方式是将你的整个项目当做一个整体,然后通过给定的一个配置文件,从这个文件开始找到你项目所依赖的文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件,如下图是工作方式:
Webpack具有的特性
1. 对 CommonJS 、 AMD 、ES6的语法做了兼容
2. 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持
3. 有独立的配置文件webpack.config.js
4. 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
5. 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活
Webpack 安装及使用
首先全局安装webpack,进入终端就可以安装,安装目录如下:
npm install -g webpack //全局安装webpack
在一个本地新建一个文件夹webpackdemo,在终端转到该文件夹下,输入如下命令:
npm init //这个命令是让你配置一些初始得到信息,例如项目名称、入口文件,作者等
完成上述以后,你的基础信息配置完成,安装局部的webpack,命令如下:
npm install --save-dev webpack
完成上述步骤,应该会有如下文件:
- node_modules:依赖包文件
- package-lock.json:当 node_modules 或 package.json 发生变化时自动生成的文件.这个文件主要功能是确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新
- package.json:刚开始配置的基础信息,以后依赖的安装包名称都会这里
回到之前的那个空的文件夹内,建立如下图所示文件以及文件夹:
app文件夹是我们的基础文件夹
public是展示的文件夹
webpack.config.js是我们的脚本配置文件
hello.js
//hello.js var config = require('../config.json') module.exports = { sayhello: function () { console.log(config.world); } }
config.json
//config.json { "world": "hello world!" }
main.js
//main.js var a = require('./hello.js'); a.sayhello();
index.html
//index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>webpack</title> <script src="bundle.js"></script> </head> <body> </body> </html>
bundle.js: 打包以后生成的文件
开始第一次打包,一般的命令如下:
webpack app/js/main.js public/bundle.js //webpack 入口文件 输入文件
结果出现了上述红色的错误信息,查明原因是因为我们webpack的版本太高了,使用如下命令:webpack app/js/main.js -o public/bundle.js
看到如图结果红色的错误信息没有了,出现了黄色的警告信息,经过研究表明,webpack有3中模式,有开发模式(develpoment),生产模式(production),无这三个状态,使用命令webpack app/js/main.js -o public/bundle.js –mode develpoment,如下图所示:
Hash代表产生的的hash值;version代表目前的webpack的版本;time是打包所用的时间;built at 完成的时间;asseet是产生的文件;size生成文件的大小;chunks是打包的分块;chunk names是打包名称;
现在看你的项目里面,publick文件夹下面是不是多了一个bundle.js文件,打开一看和我们原来的差不多,是没有压缩的文件,如果用app/js/main.js -o public/bundle.js –modeproduction命令,生成的文件是是压缩过的文件。
如果进行到了这里,那你就成功了,哈哈,是不是很简单,只要细心分析,肯定可以的
但是你有没有发现,难道我每次打包处理文件,都要这样去敲命令行,还这么多信息要输入,答案肯定会的,下面我们进行配置webpack.comfig.js(最开始创建文件时已经创建),如下代码:
module.exports = {
entry: __dirname + "/app/js/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放的地方
filename: "bundle.js" //打包后输出文件的文件名
}
}
//“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录,你可写,也可去掉
我们运行命令:webpack,如图:
看到了产生了bundle.js,但是有黄色的信息警告,我们仔细一看是,没有配置模式,打开webpack.config.js,添加代码:
module.exports = {
entry: __dirname + "/app/js/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放的地方
filename: "bundle.js" //打包后输出文件的文件名
},
mode: 'development' // 设置mode
}
然后运行webpack,发现了没有黄色的警告信息
另一种打包方式,配置package.json:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack"
}
运行命令:npm start ,和上述webpack命令一样
注:上述的两种打包都是在全局安装的几种情况下打包的 ,非全局打包:
1. node_modules/ .bin/webpack app/js/main.js public/bundle.js
2. 配置package.json,运行命令:npm start
package.json中的script会安装一定顺序寻找命令对应位置,本地的node_modules/.bin路径就在这个寻找清单中,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。
强大的调试功能(生成Source Maps)
开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的,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搭建本地服务
Webpack搭建本地的服务器,可以监听你修改的代码,并刷新显示修改后的结果,服务器是基于node.js构建,首先要装它的依赖:
npm install --save-dev webpack-dev-server
以下是服务器的配置选择,具体点击了解更多
devserver的配置选项 | 功能描述 |
---|---|
contentBase | 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public”目录) |
port | 设置默认监听端口,如果省略,默认为”8080“ |
inline | 设置为true,当源文件改变时会自动刷新页面 |
historyApiFallback | 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html |
在webpack.config.js中配置devserver
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/js/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放的地方
filename: "bundle.js" //打包后输出文件的文件名
},
mode: 'development', // 设置mode
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true //实时刷新
}
}
在package.json中配置script:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",
"server": "webpack-dev-server --open"
}
在终端输入npm run server ,如下图所示,已经启动成功,并且会打开浏览器:
运行结果:
我们修改一下config.js文件,内容改为I come from china,如下如。终端显示:
已经编译完成,生成新的文件bundle.js,然后看看浏览器,如图
在控制台已经打印出修改的结果。
Module
Module 主要是用来配置加载器(Loaders),包括loaders、preLoaders、postLoaders、noParse。webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。
Loaders
Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loaders的配置包括以下几方面:
test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
loader:loader的名称(必须)
include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
query:为loaders提供额外的设置选项(可选)
Babel
Babel其实是一个编译JavaScript的平台,让你能使用最新的JavaScript代码(ES6,ES7…),而不用管新标准是否被当前使用的浏览器完全支持,让你能使用基于JavaScript进行了拓展的语言,比如React的JSX。
如何安装Babel并且配置
babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。
如下命令安装:
// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
配置webpack.config.js
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/js/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放的地方
filename: "bundle.js" //打包后输出文件的文件名
},
mode: 'development', // 设置mode
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true //实时刷新
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}]
}
}
如上述代码,webapck已允许支持了es6和jsx语法,所以后续我们会用react,所以要安装react需要的配置信息。
终端安装:
npm install --save react react-dom
更改hello.js,返回一个react的组件
import React,{Component} from 'react'
import config from './config.json'
class hello extends Component {
render() {
return (
<div >
{config.world}
</div>
)
}
}
export default hello
更改main.js,渲染hello模块
import React from 'react'
import {render} from 'react-dom'
import Hello from './hello'
render(<Hello />, document.getElementById('root'));
更改public/index.html,添加
<div id="root"></div>
运行npm run server,出现了2处错误,第一处:
如上图看出,是版本出现了问题,打开package.json,发现babel-core的版本是6.26.3,凡是babel-loader的版本是8.X,并且提示了如果要用babel-core 的6.X版本,所以要装babel-loader的7.X的版本,所以运行终端:nom install –save-dev babel-loader@7 , 然后在运行npm run server,发现终端没有错误了,但是浏览器报错了,如图:
仔细分析,原来是我们的js引入有问题,我将webpack生成的js文件放在了head,此时DOM还没有建立完毕,因此出现 not a DOM element 的错误,所以将bundle.js文件放在HTML底部就可以了。
然后运行,发现成功了,没有报错的信息,浏览器所示:
由于Babel在webpack.config.js里配置,考虑到babel具有非常多的配置选项,并且会使webpack.config.js的文件越来越复杂,所以我们单独新建一个文件”.babelrc”,和webpack.config.js在同一级,如图所示更改:
CSS模块
Webpack有两个依赖包来处理样式表(css-loader 和 style-loader),这里我们只引入css文件(不引入sass和less,因为和css引入方式相同),二者的处理方式不同:
style-loader:style-loader能够在需要载入的html中创建一个标签,标签里的内容就是CSS内容
css-loader:css-loader是允许在js中import一个css文件,会将css文件当成一个模块引入到js文件中
安装(css-loader和 style-loader)
npm install --save-dev style-loader css-loader
在css文件夹中创建main.css,并输入如下内容:
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 18px;
background-color: beige;
}
h1, h2, h3, h4, h5, h6, p, ul {
margin: 0;
padding: 0;
}
在主入口文件(main.js)中导入main.css
import React from 'react'
import {render} from 'react-dom'
import Hello from './hello'
import '../css/main.css' // 导入css
render(<Hello />, document.getElementById('root'));
运行npm start,发现你加入的css文件已经被引入了,并且生效了。
那这里有出现一个问题,大多数的样式表,都有很多的全局变量名称,每个开发者都有可能写两个或者以上的相同的名称,以后的维护和修改都比较困难,怎么去避免全局变量名的污染等,答案是肯定可以的,引入了css module,有兴趣可以访问官方文档
Css module技术是把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack对CSS模块化提供了非常好的支持,只需要在CSS loader中进行简单配置即可,然后就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染。下面我们进行配置:
{// 指定启用css modules
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}
]
}
在css文件夹中创建 hello.css,加入如下代码:
.rootchild{
font-size: 20px;
color:red;
}
hello.js更改如下:
import React,{Component} from 'react'
import config from './config.json'
import helloone from '../css/hello.css' //引入css
class hello extends Component {
render() {
return (
<div className={helloone.rootchild}> {/* className 是添加类名 */}
{config.world}
</div>
)
}
}
export default hello
运行npm start,如图所示:发现字体的颜色发生了变化,并且类名也发生了变化,这样相同的类名也不会造成不同组件之间的污染。
插件学习(Plugins)
插件是webpack的拓展功能,作用于整个构建的过程,很多人把loader和插件分不清楚,你只要记住,loader是处理源文件的,一次处理一个,而插件不处理单个文件,是处理整个项目的构成,webpack有很多的内置插件,也有很多的第三方插件,如果是第三方,是需要手动去安装,再引用
红色的部分是添加的部分,如图打包以后:
HtmlWebpackPlugin
这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如添加了hash值)。
先对原来的文件夹进行一些改动:移除public文件夹,在app文件夹下创建一个index.tmpl.html的文件模板,在使用插件的时候,会自动依据模板生成一个新的index.html。Index.tmpl.html 模板如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
安装HtmlWebpackPlugin
npm install --save-dev html-webpack-plugin
更新配置(webpack.config.js):
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/js/main.js", //唯一入口文件
output: {
path: __dirname + "/build", //打包后的文件存放的地方
filename: "bundle.js" //打包后输出文件的文件名
},
mode: 'development', // 设置mode
devServer: {
contentBase: __dirname + './build', //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true //实时刷新
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}]
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
})
]
}
引入插件名称,修改输出地址,在plugins里添加,运行npm start
发现文件夹里多了一个build的文件夹,打开文件,发现有一个index.html和build.js文件。用浏览器打开index.html,发现和原来public文件夹里的事一样的,这样打包更加的方便。
如果你要实时更新你的代码,一定要配置好服务的内容devServer。
优化插件
webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能
- OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
- UglifyJsPlugin:压缩JS代码,这个因为高版本的需求,已经不用这个插件了(下面的配置中,我会配置)
- ExtractTextPlugin:分离CSS和JS文件
由于第一个和第二个是内置插件,只要引入就可以了,但是后面分离css和js,需要安装插件:
npm install --save-dev extract-text-webpack-plugin@next
为什么要加@next,因为extract-text-webpack-plugin目前版本不支持webpack4,下面是配置了组件分配Id,压缩js,分离了css和js:
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/js/main.js", //唯一入口文件
output: {
path: __dirname + "/build", //打包后的文件存放的地方
filename: "bundle.js" //打包后输出文件的文件名
},
mode: 'development', // 设置mode
devServer: {
contentBase: __dirname + './build', //本地服务器所加载的页面所在的目录
historyApiFallback: true, //不跳转
inline: true //实时刷新
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
modules: true,
localIdentName: '[name]__[local]--[hash:base64:5]'
}
}]
})
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new ExtractTextPlugin("style.css")
],
/* 压缩js代码 */
optimization: {
minimize: true
}
}
//“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
运行npm start ,发现build文件夹里多出了style.css。
引入图片
首先还是要装插件,终端:
npm install --save-dev url-loader
Hello.css
.rootchild{
font-size: 20px;
color:red;
background-image: url('../img/hello.jpg');
width: 197px;
height: 197px;
}
Webpack.config.js
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader?limit=8192&name=img/[hash:8].[name].[ext]'
}
运行代码:npm start ,浏览器打开build文件夹下的index.html,如图:
结束语
写完这篇文章,想起当时搞这个基础配置,也花费了几天的时间,总是搞不好,版本的问题,配置问题,反正各种问题,也是各个博客去看自己错在什么地方,搞的人头都大了,总之当时功夫不负有心人,当时是成功了。这篇文章是比较基础的,对于新手入门还是很重要,只要按照流程,还是很容易的搭建出来,文章里有很多容易出错的点,我也列举出来了,对于点的介绍都很全面,如果看到这里,说明你已经基本入门了,可以很好地去了解其他关于webpack的知识点。
本文的代码在这里webpackbasic
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 JsOcean的博客!