在使用webpack
以前咱们要安装webpack
,安装命令以下:css
# 建立webpack-demo文件夹
mkdir webpack-demo
# 生成package.json文件,对项目依赖进行管理
yarn init -y
# 安装webpack相关依赖
yarn add webpack webpack-cli -D
复制代码
这里咱们安装webpack-cli
的缘由是由于它可让咱们能够在命令行中运行webpack
,不然webpack
命令将没法运行html
接下来咱们创建src
目录来管理咱们的源代码,并创建main.js
文件,写入第一行代码:vue
console.log('Hello webpack!')
复制代码
接下来咱们在项目根目录创建webpack
的配置文件webpack.config.js
并进行最简单的配置:node
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/main.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'main.js'
}
}
复制代码
以后咱们经过package.json
文件中的script
命令来为webpack
添加快捷命令:
react
配置好后经过命令行执行:yarn build
,会发现根目录出现了dist
目录,里边有咱们的代码main.js
。webpack
接下来咱们在根目录新建index.html
,经过script
标签将打包后的代码引入并在浏览器中打开:
es6
或者能够经过node
命令来运行打包文件:
web
到这里咱们成功打包好了咱们的第一个文件,感受仍是有些小激动的。chrome
在配置以前,咱们先简单理解下入口和出口的概念:npm
entry
): 整个项目的起始模块,用来做为构建其内部依赖图的开始output
): 告诉webpack
打包后的文件所存放的目录这里是一个打包截图:
了解了基本概念以后,咱们来看一下经常使用的相关配置:
entry: {
main: './src/main.js' // 入口文件对应的源代码位置,key值为打包生成后的chunkNames
},
output: {
path: path.resolve(__dirname, './dist'), // 打包生成文件存放的位置
// 使用每次构建过程当中,惟一的hash生成
filename: '[name]_[hash].js', // 每一个打包输出文件的名称
// publicPath: 'https://cdn.example.com/assets/', // 会在引入的资源前加入该路径,例:将资源托管到cnd
}
复制代码
在配置文件中咱们使用了[name]
这种符号,这是webpack
中的占位符(placeholder
),这里介绍下配置中使用到的,若是小伙伴们感兴趣能够本身查阅官方文档:
[name]
: 模块名称[hash]
: 模块标识符的hash
(每次打包都会生成对应惟一的hash
值)在webpack
中,咱们能够经过mode
选项,来分别为生成环境打包和开发环境打包使用相应的内置优化:
mode: 'development'
复制代码
目前咱们的配置是开发环境: development
,当咱们使用production
的时候,webpack
会将我node
中的全局变量改成: process.env.NODE_ENV
的值设置为production
,而且也会启用代码压缩等功能,有助于帮咱们减小代码体积,提高用户体验。
而当咱们使用development
模式时,webpack
会将process.env.NODE_ENV
的值设置为development
,并取消代码压缩等功能,提高开发体验
在webpack
中,咱们可使用各类各样的插件来自定义webpack
构建过程,方便咱们的开发。
这里咱们介绍2个经常使用的plugin
:
HtmlWebpackPlugin
: 自动建立一个html
文件来帮咱们引入打包文件,这对咱们每次打包都经过hash
值来生成不一样的打包文件的状况特别有用CleanWebpackPlugin
: 在打包以前删除output.path
指定的位置中的文件,保证每次打包都是最新的文件首先咱们来安装这俩个插件:
yarn add --dev html-webpack-plugin clean-webpack-plugin
复制代码
接下来咱们再webpack
中使用这2个插件:
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html', // 生成html文件的文件名
template: './index.html' // 使用的html模板
}),
new CleanWebpackPlugin()
]
复制代码
这里咱们经过为html-webpack-plugin
来指定根目录下的index.html
为模版,生成打包后的index.html
。新生成的index.html
文件与模板文件不一样之处在于会自动引入打包生成的文件(包括js
文件、css
文件、以及以后配置的dll
文件),这样即便咱们使用了[hash]
等占位符,插件也会自动帮咱们动态引入资源,而不用咱们每次手工配置。
使用HtmlWebpackPlugin
指定template
的缘由是由于咱们能够在模板文件中本身写一些咱们本身的代码,好比引入一些css
或者执行一段js
脚本亦或者咱们能够在模板文件中指定项目的根元素:
<!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>Document</title>
</head>
<body>
<!-- 在模板中指定根元素,方便开发时为其中插入元素 -->
<div id="root"></div>
</body>
</html>
复制代码
经过使用插件咱们目前解决了以下问题:
loader
下面是对webpack
官网中loader
介绍的一步分摘抄:
loader
用于对模块的源代码进行转换
特性(这里只列出了部分):
loader
支持链式传递,链中每一个loader
会将转换应用在已处理过的资源上。一组链式的loader
将按照相反的顺序执行。链中的第一个loader
将其结果(也就是转换后的资源)传递给下一个loader
,依次类推loader
能够经过options
对象配置- 插件能够为
loader
带来更多特性
我我的以为其实loader
就是让webpack
能够识别各类资源,而后将资源加工处理成浏览器能够识别的、兼容性更好的、性能更好的代码。
接下来咱们学习如何经过loader
来让webpack
处理各类资源。
css
要想使用css
文件,咱们首先须要安装
style-loader和
css-loader`:
yarn add style-loader css-loader -D
复制代码
在webpack.config.js
进行配置:
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
}
复制代码
可是平常的项目中,有不少css
属性须要添加浏览器供应商前缀来确保兼容性, 咱们可使用postcss-loader
结合autoprefixer
来实现:
yarn add postcss-loader autoprefixer -D
复制代码
咱们须要为css
文件添加postcss-loader
,以后在根目录新建postcss.config.js
配置autoprefixer
:
// webpack.config.js
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
// postcss.config.js
module.exports = {
plugins: [
// autoprefixer: parse CSS and add vendor prefixes to CSS rule using values from can I use
// 解析CSS并使用Can I use 中的值 将供应商前缀添加到css规则中
require('autoprefixer')
]
};
复制代码
日常咱们也会用到css
预处理器来方便开发,这里咱们以sass
为例:
yarn add sass-loader node-sass -D
复制代码
在webpack
中添加以下配置:
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
}
复制代码
这里要特别注意loader
的顺序问题(反向:从下到上,从右到左):
sass-loader
将scss
文件中的语法解析为css
语法postcss-loader
为对应的css
属性添加浏览器供应商前缀css-loader
根据import
语法将全部的css
文件整合到一块儿css-loader
整合的css
文件经过style
标签插入到html
中,实现样式的更改若是顺序书写错误,会致使程序没法正常运行
最终效果以下:
学习了上面的知识之后,咱们再了解几个css-loader
的经常使用配置:
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// 开启css模块化
modules: true,
// 在css-loader前应用的loader的数量:确保在使用import语法前先通过sass-loader和postcss-loader的处理
importLoaders: 2
}
},
'postcss-loader',
'sass-loader'
]
}
复制代码
要想在项目中使用图片和字体图标,咱们须要使用到file-loader
:
yarn add file-loader -D
复制代码
须要在webpack.config.js
中添加以下配置:
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
复制代码
配置成功后webpack
会将咱们引入的资源转换为路径字符串,webpack
会经过字符串帮咱们找到文件的位置。
接下来咱们尝试一下file-loader
的几个配置项:
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
// placeholders:
// [ext]: 资源扩展名
// [name]: 资源的基本名称
// [hash]: 内容hash值
// [path]: 资源相对于context的路径
// 默认值: [hash].[ext]
name: '[name]_[hash:8].[ext]',
// 打包文件存放到出口目录下的images文件中
outputPath: 'images/'
}
}
]
}
复制代码
打包文件以下:
这里也可使用url-loader
来处理静态资源,url-loader
的工做方式和file-loader
相同,可是它会在文件小于限制大小(单位byte
)的时候,返回base64
字符串,当大于限制大小时会返回和file-loader
同样的地址字符串。
这在处理一些小文件的时候不用再进行资源请求,能够提升性能
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// placeholders:
// [ext]: 资源扩展名,默认file.extname
// [name]: 资源的基本名称,默认file.basename
// [hash]: 内容hash值,默认md5
// [path]: 资源相对于context的路径,默认值file.dirname
// 默认值: [hash].[ext]
name: '[name]_[hash:8].[ext]',
// 打包文件存放到出口目录下的images文件中
outputPath: 'images/',
limit: 8192, // 单位byte,文件小于8kb时返回base64文件,大于这个限制会返回地址
}
}
]
}
复制代码
咱们能够看一下打包区别:
source map
配置当咱们使用webpack
进行代码打包之后,若是代码出现错误,会很难追踪到错误和警告在源代码中的原始位置。为了更容易地追踪错误和警告,JavaScript
提供了SourceMap
功能,能够将编译后的代码映射回原始源代码。
webpack
中配置以下:
// 此选项控制是否生成,以及如何生成source map
devtool: 'none',
复制代码
可选项以下:
这里简单介绍下各配置项中单词的一些含义,方便理解:
cheap
: 不会生成列映射,只会映射行数eval
: 每一个模块都使用eval
执行,而且都有//@sourceURL
module
:会处理来自loader
的source map
inline
: source map
转换为DataUrl
后添加到bundle
中平常的开发中咱们会进行以下配置:
cheap-module-eval-source-map
cheap-module-source-map
(生产环境使用source map
,方便定位错误)在开发环境和生产环境中,咱们要用到的webpack
配置是不一样的,这里咱们在根目录下分别新建webpack.dev.js
和webpack.prod.js
来分别存放开发环境和生产环境所须要的配置,经过原有的webpack.config.js
来存放公共的配置文件。
接下来咱们使用webpack-merge
来进行配置文件的合并工做:
yarn add webpack-merge -D
复制代码
为了方便统一管理,咱们根目录下新建config
文件夹,而后将配置文件放到里边,如今的配置文件是这样的:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const absPath = (dir) => path.resolve(__dirname, dir)
module.exports = {
entry: {
main: absPath('../src/main.js') // 入口文件对应的源代码位置,key值为打包生成后的chunkNames
},
output: {
path: absPath('../dist'), // 打包生成文件存放的位置
// 使用每次构建过程当中,惟一的hash生成
filename: '[name]_[hash].js', // 每一个打包输出文件的名称
// publicPath: 'https://cdn.example.com/assets/', // 会在引入的资源前加入该路径,例:将资源托管到cnd
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
]
},
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// 开启css模块化
modules: true,
// 在css-loader前应用的loader的数量:确保在使用import语法前先通过sass-loader和postcss-loader的处理
importLoaders: 2
}
},
'postcss-loader',
'sass-loader'
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// placeholders:
// [ext]: 资源扩展名,默认file.extname
// [name]: 资源的基本名称,默认file.basename
// [hash]: 内容hash值,默认md5
// [path]: 资源相对于context的路径,默认值file.dirname
// 默认值: [hash].[ext]
name: '[name]_[hash:8].[ext]',
// 打包文件存放到出口目录下的images文件中
outputPath: 'images/',
limit: 8192, // 单位byte,文件小于8kb时返回base64文件,大于这个限制会返回地址
}
}
]
}
]
},
plugins: [
// 自动引入打包后的文件到html中:
// 对于每次打包都会从新经过hash值来生成文件名的状况特别适用
// 也能够经过template来生成一个咱们本身定义的html模板,而后帮咱们把打包后生成的文件引入
new HtmlWebpackPlugin({
filename: 'index.html', // 生成html文件的文件名
template: absPath('../index.html') // 使用的html模板
})
]
};
// webpack.dev.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config')
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'cheap-module-eval-source-map'
})
// webpack.prod.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
devtool: 'cheap-module-source-map',
plugins: [
new CleanWebpackPlugin()
]
})
复制代码
而后咱们在package.json
中分别为开发环境和生产环境添加打包快捷命令:
"scripts": {
"build": "webpack --config ./config/webpack.prod.js",
"start": "webpack --config ./config/webpack.dev.js"
},
复制代码
如今目录结构是这样的:
接下来咱们就能够直接使用yarn build
和yarn start
命令来分别为开发环境和生产环境进行打包了
webpackDevServer
方便开发和调试如今咱们在每次修改代码后,都须要再次从新打包文件,并在浏览器中打开生成的html
文件。并且因为是本地打开html
文件,诸如请求代理、局域网内预览等功能便没法使用。
webpack-dev-server
为咱们提供了一个简单的web
服务器,可以实时从新加载(live reloading
)修改后的代码,而且也提供了如proxy
等功能来支持咱们跨域请求接口,极大的方便了开发。
yarn add webpack-dev-server -D
复制代码
以后咱们在开发环境的webpack
配置中添加以下配置:
devServer: {
// 告诉服务器从哪里提供内容。只有在你想要提供静态文件时才须要
contentBase: absPath('../dist'),
// 是否在启动服务时自动打开浏览器
open: true,
// 端口在没有设置的时候默认为8080
port: 3000
}
复制代码
固然,这样更改以后咱们要经过webpack-dev-server
来执行开发时的配置文件:
// "start": "webpack --config ./config/webpack.dev.js"
"start": "webpack-dev-server --config ./config/webpack.dev.js"
复制代码
执行yarn start
:
当咱们在开发过程当中修改一些代码的时候,咱们可能只是想让咱们当前修改的内容生效,而不是形成整个页面的刷新,致使咱们每次修改都要从新以前的步骤来验证咱们的代码。
在webpack
中咱们经过以下配置来实现模块热替换的功能(hot module replacement
),固然这个功能只在开发环境开启:
// webpack.dev.js
devServer: {
// 告诉服务器从哪里提供内容。只有在你想要提供静态文件时才须要
contentBase: absPath('../dist'),
// 是否在启动服务时自动打开浏览器
open: true,
// 端口在没有设置的时候默认为8080
port: 3000,
// 启用webpack的模块热替换特性
hot: true
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
复制代码
和以前配置的区别有2个:
devServer
添加配置项: hot: true
webpack
的插件: webpack.NameModulesPlugin
和webpack.HotModuleReplacementPlugin
而后这样只能实现css
文件的热更新,对于js
的热更新,咱们还须要添加一下代码:
// accept接收2个参数: 1. dependencies: 一个字符串或字符串数组 2. callback: 模块更新后触发的函数
if (module.hot) {
module.hot.accept('./utils/printSomething', () => {
console.log('update module');
})
}
复制代码
页面中的效果以下:
你们可能会发现咱们在写css
和相似于vue
和react
框架代码的时候,并无本身手写module.hot.accept
方法,这是框架和css
的loader
已经帮咱们进行了自动处理,咱们只须要关注代码的书写便可。
babel
转义es6
语法在平常工做中,咱们会使用不少es6
里的新语法,这些语法在咱们目前使用的chrome
新版浏览其中通常均可以很好的支持,可是在一些国产浏览器或者低版本浏览器中可能会出现兼容性问题。
这里咱们须要经过babel-loader
来进行语法的转义:
yarn add babel-loader @babel/core @babel/preset-env -D
复制代码
在webpack
中进行配置:
// webpack.config.js 中添加一个loader配置项
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
},
复制代码
项目根目录创建.babelrc
文件,并加入以下代码:
{
"presets": [
"@babel/preset-env"
]
}
复制代码
这样配置以后,项目中的Promise
,map
等语法依旧不会进行转换,这里咱们使用@babel/polyfill
来转换这些语法:
yarn add @babel/polyfill -D
复制代码
.babelrc
进行以下配置:
{
"presets": [
[
"@babel/env",
{
// 为低版本引入babel/polyfill
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
// 使用useBuiltIns要指定corejs
"corejs": "2",
// 只对项目中用到的`esnext`语法进行polyfill处理
"useBuiltIns": "usage",
}
]
]
}
复制代码
这样配置以后就能够经过babel
来转换esnext
的一些语法,而且能够兼容低版本和国产浏览器。因为使用到了useBuiltIns:usage
,只会对咱们使用到的新语法进行转换,减小了polyfill
的体积
react
开发环境在学习完以上内容之后,咱们须要搭建一个支持react
框架语法的webpack
配置。
首先咱们安装相应的依赖:
yarn add react react-dom
yarn add @babel/preset-react @babel/plugin-proposal-class-properties -D
复制代码
.babelrc
中配置以下:
{
"presets": [
[
"@babel/env",
{
// 为低版本引入babel/polyfill
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
// 使用useBuiltIns要指定corejs
"corejs": "2",
// 只对项目中用到的`esnext`语法进行polyfill处理
"useBuiltIns": "usage",
}
],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
复制代码
以后咱们在入口文件main.js
中写入以下代码:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
number: 10
}
render() {
return (
<div> hello Webapck React <h2>{this.state.number}</h2> </div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root')) 复制代码
执行yarn start
命令:
到这里为止,咱们已经能够搭建一个简单的react
开发环境,以后咱们能够继续学习一些webpack
的高级概念。