webpack常用来处理前端工程化问题:包括自动化打包、es6转换、图片处理、css样式压缩等
两种方式:全局和本地
npm intall -g webpack webpack-cli
// or
yarn global add webpack webpack-cli
使用:
使用webpack对文件进行编译,如当前目录存在index.js文件:
webpack index.js
// or
webpack index.js --mode development
// or
webpack index.js --mode production
注意:webpack默认查找路径为项目根路径下的src
全局安装模式下,当项目放到其他环境时无法通过npm install安装项目所需依赖,因为依赖在全局环境中,没有写入package.json文件中
本地安装解决以上问题
// 初始化package.json
npm init -y
// 安装webpack
yarn add -D webpack webpack-cli
修改package.json文件:
"scripts":{
"build":"webpack"
}
项目目录:web01/src/index.js
启动打包
yarn build
// or
npm run build
注意:此时会有提示没有设置mode,可以在package.json中设置,也可以单独配置(配置文件中的mode字段)
"scripts":{
"build":"webpack --mode production"
}
需求引入:不光是main.js,如果需要连同html一起生成,则必须通过webpack的配置才行
需要生成你html文件,必须安装htmlwebpackplugin插件
npm install -D html-webpack-plugin
然后配置:web01/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports={
entry: "./src/index.js",/* 以配置文件开始 */
plugins:[
new HtmlWebpackPlugin()
]
}
然后打包,即可看到index.html
一个完整的webpack.config.js包括:
const path = require('path');
module.exports = {
mode: "production", // "production" | "development" | "none" // Chosen mode tells webpack to use its built-in optimizations accordingly.
entry: "./app/entry", // string | object | array // 这里应用程序开始执行
// webpack 开始打包
output: {
// webpack 如何输出结果的相关选项
path: path.resolve(__dirname, "dist"), // string
// 所有输出文件的目标路径
// 必须是绝对路径(使用 Node.js 的 path 模块)
filename: "bundle.js", // string // 「入口分块(entry chunk)」的文件名模板(出口分块?)
publicPath: "/assets/", // string // 输出解析文件的目录,url 相对于 HTML 页面
library: "MyLibrary", // string,
// 导出库(exported library)的名称
libraryTarget: "umd", // 通用模块定义 // 导出库(exported library)的类型
/* 高级输出配置(点击显示) */ },
module: {
// 关于模块配置
rules: [
// 模块规则(配置 loader、解析器等选项)
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, "app")
],
exclude: [
path.resolve(__dirname, "app/demo-files")
],
// 这里是匹配条件,每个选项都接收一个正则表达式或字符串
// test 和 include 具有相同的作用,都是必须匹配选项
// exclude 是必不匹配选项(优先于 test 和 include)
// 最佳实践:
// - 只在 test 和 文件名匹配 中使用正则表达式
// - 在 include 和 exclude 中使用绝对路径数组
// - 尽量避免 exclude,更倾向于使用 include
issuer: { test, include, exclude },
// issuer 条件(导入源)
enforce: "pre",
enforce: "post",
// 标识应用这些规则,即使规则覆盖(高级选项)
loader: "babel-loader",
// 应该应用的 loader,它相对上下文解析
// 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
// 查看 webpack 1 升级指南。
options: {
presets: ["es2015"]
},
// loader 的可选项
},
{
test: /\.html$/,
test: "\.html$"
use: [
// 应用多个 loader 和选项
"htmllint-loader",
{
loader: "html-loader",
options: {
/* ... */
}
}
]
},
{ oneOf: [ /* rules */ ] },
// 只使用这些嵌套规则之一
{ rules: [ /* rules */ ] },
// 使用所有这些嵌套规则(合并可用条件)
{ resource: { and: [ /* 条件 */ ] } },
// 仅当所有条件都匹配时才匹配
{ resource: { or: [ /* 条件 */ ] } },
{ resource: [ /* 条件 */ ] },
// 任意条件匹配时匹配(默认为数组)
{ resource: { not: /* 条件 */ } }
// 条件不匹配时匹配
],
/* 高级模块配置(点击展示) */ },
resolve: {
// 解析模块请求的选项
// (不适用于对 loader 解析)
modules: [
"node_modules",
path.resolve(__dirname, "app")
],
// 用于查找模块的目录
extensions: [".js", ".json", ".jsx", ".css"],
// 使用的扩展名
alias: {
// 模块别名列表
"module": "new-module",
// 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
"only-module$": "new-module",
// 起别名 "only-module" -> "new-module",但不匹配 "only-module/path/file" -> "new-module/path/file"
"module": path.resolve(__dirname, "app/third/module.js"),
// 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
// 模块别名相对于当前上下文导入
},
/* 可供选择的别名语法(点击展示) */
/* 高级解析选项(点击展示) */ },
performance: {
hints: "warning", // 枚举 maxAssetSize: 200000, // 整数类型(以字节为单位)
maxEntrypointSize: 400000, // 整数类型(以字节为单位)
assetFilter: function(assetFilename) {
// 提供资源文件名的断言函数
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},
devtool: "source-map", // enum // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
// 牺牲了构建速度的 `source-map' 是最详细的。
context: __dirname, // string(绝对路径!)
// webpack 的主目录
// entry 和 module.rules.loader 选项
// 相对于此目录解析
target: "web", // 枚举 // 包(bundle)应该运行的环境
// 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
externals: ["react", /^@angular\//], // 不要遵循/打包这些模块,而是在运行时从环境中请求他们
stats: "errors-only", // 精确控制要显示的 bundle 信息
devServer: {
proxy: { // proxy URLs to backend development server
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
compress: true, // enable gzip compression
historyApiFallback: true, // true for index.html upon 404, object for multiple paths
hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
https: false, // true for self-signed, object for cert authority
noInfo: true, // only errors & warns on hot reload
// ...
},
plugins: [
// ...
],
// 附加插件列表
/* 高级配置(点击展示) */}
https://www.webpackjs.com/configuration/
入口文件,可以是单一入口,和多入口:string,array,object
多入口可以拆分代码将每个页面需要的js分离出来,使用对象作为entry时可以规定输出文件的名字!
// 可以是如下值
entry: "./app/entry",
entry: ["./app/entry1", "./app/entry2"],
entry: {
a: "./app/entry-a",
b: ["./app/entry-b1", "./app/entry-b2"]
},
指定输出文件的名字和其他属性
output: {
// webpack 如何输出结果的相关选项
path: path.resolve(__dirname, "dist"), // string
// 所有输出文件的目标路径
// 必须是绝对路径(使用 Node.js 的 path 模块)
filename: "bundle.js", // string
filename: "[name]xxx.js", // 用于多个入口点(entry point)(按照entry定义的名字输出)
filename: "[chunkhash].js", // 用于长效缓存
// 「入口分块(entry chunk)」的文件名模板(出口分块?)
publicPath: "/assets/", // string
publicPath: "",
publicPath: "https://cdn.example.com/",
// 输出解析文件的目录,url 相对于 HTML 页面
library: "MyLibrary", // string,
// 导出库(exported library)的名称
libraryTarget: "umd", // 通用模块定义,或:
// 导出库(exported library)的类型
/* 高级输出配置(点击显示) */
},
filename: "[hash:4].js
指定4位哈希码,可以解决浏览器缓存不更新文件的情况,如果改变文件内容(此处hash会将entry中入口的文件组合在一起hash),会导致编译出来的hash码不相同,同一次编码出来的hash相同。
path字段要求必须是绝对路径,所以可以引入node的path模块:
const path = require("path")
module.exports = {
entry:{...},
output:{
filename:xxxx,
path:path.join(__dirname,'output')
}
}
此处path.join(__dirname,'output')
相当于将输出放在根目录下的output文件夹中
publicPath字段:在开发时有可能使用本地路径的图片,而生产环境中可能需要使用cdn加速来获取图片,该字段可以在编译输出文件时自动替换url
loader 特性
options
对象进行配置。package.json
常见的 main
属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json
里定义一个 loader
字段。loader 通过(loader)预处理函数,为 JavaScript 生态系统提供了更多能力。 用户现在可以更加灵活地引入细粒度逻辑,例如压缩、打包、语言翻译和其他更多。
解析 loader
loader 遵循标准的模块解析。多数情况下,loader 将从模块路径(通常将模块路径认为是 npm install
, node_modules
)解析。
loader 模块需要导出为一个函数,并且使用 Node.js 兼容的 JavaScript 编写。通常使用 npm 进行管理,但是也可以将自定义 loader 作为应用程序中的文件。按照约定,loader 通常被命名为 xxx-loader
(例如 json-loader
)。有关详细信息,请查看 如何编写 loader?。
使用loader来加载css文件和less文件:
npm install --save-dev css-loader
npm install --save-dev less-loader
webpack.config.js
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: ['style-loader','css-loader'] },
{ test: /\.less$/, use: ['style-loader','css-loader','less-loader']}
]
}
};
module.rules
允许你在 webpack 配置中指定多个 loader。 这是展示 loader 的一种简明方式,并且有助于使代码变得简洁。同时让你对各个 loader 有个全局概览:
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
使用 !
将资源中的 loader 分开。分开的每个部分都相对于当前目录解析。
import Styles from 'style-loader!css-loader?modules!./styles.css';
通过前置所有规则及使用 !
,可以对应覆盖到配置中的任意 loader。
选项可以传递查询参数,例如 ?key=value&foo=bar
,或者一个 JSON 对象,例如 ?{"key":"value","foo":"bar"}
。
尽可能使用
module.rules
,因为这样可以减少源码中的代码量,并且可以在出错时,更快地调试和定位 loader 中的问题。
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
这会对 .jade
文件使用 jade-loader
,对 .css
文件使用 style-loader
和 css-loader
。
插件用于解决loader无法实现的事情
webpack 插件是一个具有 apply
属性的 JavaScript 对象。apply
属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, compilation => {
console.log("webpack 构建过程开始!");
});
}
}
compiler hook 的 tap 方法的第一个参数是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中复用。
通过在webpack.config.js中的plugins中new一个对象出来
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装
const ConsoleLogOnBuildWebpackPlugin = require('ConsoleLogOnBuildWebpackPlugin'); //访问内置的插件
const path = require('path');
const config = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader'
}
]
},
plugins: [
new ConsoleLogOnBuildWebpackPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
我们可以使用tsc转换工具来转换ts代码到js:
npm install -g tsc
tsc src/index.ts
如果使用webpack来转换的话:
先安装ts-loader和typescript
yarn add -D ts-loader typescript
创建ts解析文件:tsconfig.json
{
"compilerOption":{
"sourceMap":true,
}
}
此时yarn build即可生成js文件
常规初始化项目,然后在项目中安装webpack-dev-server
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');
module.exports = {
entry: {
- app: './src/index.js',
- print: './src/print.js'
+ app: './src/index.js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
+ hot: true
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Hot Module Replacement'
}),
+ new webpack.NamedModulesPlugin(),
+ new webpack.HotModuleReplacementPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
在package.json中
"scripts":{
"build":"webpack",
"dev":"webpack-dev-server"
}
然后:yarn dev
通过Node.js API来热部署,首先创建:
dev-server.js
const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.config.js');
const options = {
contentBase: './dist',
hot: true,
host: 'localhost'
};
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);
server.listen(5000, 'localhost', () => {
console.log('dev server listening on port 5000');
});
然后:node dev-server.js
在项目根目录新建两个配置文件:
在package.json文件中定义两个脚本命令:
"scripts":{
"dev":"webpack --config webpack.dev.config.js",
"build":"webpack --config webpack.prod.config.js"
}
用npm或者yarn安装好webpack及其相关包
html-webpack-plugin #生成html文件
css-loader
style-loader
less
less-loader
file-loader # 图片相关
clean-webpack-plugin # 清理生成目录
extract-text-webpack-plugin # 导出css文件,注意,如果此处使用该导出工具导出,则在module的rules中需要改变内容:
改变前:
rules:[
{
test:/\.css$/,
loader:['style-loader','css-loader']
},
{
test:/\.(jpg|png|svg)/,
loader:['file-loader']
}
]
改变后:
rules:[
{
test:/\.css$/,
use:ExtractTextWebpackPlugin.extract({
fallback:'style-loader',
use:'css-loader'
})
},
{
test:/\.(jpg|png|svg)/,
loader:['file-loader']
}
]
在每个配置文件中配置一套就可以了
但是,这样配置会导致很多相同的代码冗余,为了解决该情况,可以使用webpack-merge
使用方法:
yarn add -D webpack-merge
然后新建一个公有部分的配置文件,这里比如为base
webpack.base.config.js
webpack.prod.config.js
webpack.dev.config.js
这样,将公有部分放在base文件中,其他部分定义独有的即可
const WebpackMerge=require('webpack-merge')
const baseConfig=require('/webpack.base.config')
module.exports=WebpackMerge(baseConfig,{
// 之前module export部分
})
安装webpack、webpack-cli
安装html-webpack-plugin
新建src/index.js和src/index.html
在index.html中定义根节点
安装react、react-dom
安装babel-loader、@babel/core、@babel/preset-react、@babel/preset-env
安装css-loader、style-loader、less、less-loader
配置webpack.config.js
module.exports={
mode:"development",
entry:"./src/index.js",
module:{
rules:[
{
test:/\.(js|jsx)$/,
use:[
{
loader:"babel-loader",
options:{
presets:[
"@babel/preset-react",
"@babel/preset-env",
]
}
}
]
},
{
test:/\.css$/,
use:["style-loader","css-loader"]
},
{
test:/\.less/,
use:["style-loader","css-loader","less-loader"]
}
]
}
}
编写一个react组件开始使用