场景:项目发布时需要将根目录下的html文件和dist目录中所有文件打包上传,上线后迭代维护还需确保html代码中的资源文件正确。这样的做法非常麻烦,如果打包结果输出的目录或文件名发生变化,那么html代码中对应的script标签需要我们手动修改路径。
前端项目的模块化开发 AMD, CommonJS , ES2015 import 等等方案,大多并不直接被浏览器支持,需要我们用 babel 将 jsx转换为js,将 scss,less转换为css;除了Js代码需要模块化,HTML和CSS这些资源文件也需要被模块化。随着项目越来越复杂、代码体积越来越大, 我们要找到优化,压缩,分割方案实现前端工程化。
webpack是一个前端模块化打包解决方案,又是一个可以融合运用各种前端新技术的平台,可以将零散的JS代码打包到一个JS文件中
对于环境兼容,webpack可以先编译转换,然后再进行打包
对于不同类型的前端模块,webpack支持在JS中以模块化的方式载入任意类型的资源文件;例如在js中加载css文件,被加载的css文件将会通过style标签的方式工作
还具备代码拆分能力,能够将应用中的所有模块按需分块打包;不用担心单个文件过大,导致加载慢的问题。
一些知名的脚手架工具,也大多基于webpack(比如create-react-app)
假设现在我们手中有一系列相互关联的文件js,jsx,css,less,jpg
webpack实际上运行于node.js 其本身只能理解js文件
webpack默认会自动从src/index.js文件开始打包
//webpack.config.js
const path = require("path")
const {CleanWebpackPlugin} = require("clean-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry:"./src/index.js",
output:{
filename:"bundle.js",
},
mode:"none",
plugins:[
new CleanWebpackPlugin()
new HtmlWebpackPlugin({
title:"hello webpack",
template:"./src/index.html"
})
new CopyWebpackPlugin([
"public"//需要拷贝的目录或者路径
])
]
}
webpack核心 entry output module chunk loader plugin
entry:{
main:'./src/index.js',
second:'./src/index2.js',
vendor: ['react','react-dom']
}
我们都另存过文件,当我们另存一个文件时,我们需要确定另存的文件名和另存的路径,webpack 将打包后的结果导出的过程就类似于此,此过程由 output 配置项控制,其最基本配置包括filename和path两项。这两项用以决定上述主js文件的存储行为
output:{
path: path.join(__dirname,'./dist'),
name:'js/bundle-[name]-[hash].js',
chunkFilename:'js/[name].chunk.js',
publicPath:'/dist/'
}
占位符有多种,常见的如下:
[name]:代表打包后文件的名称,在entry或代码中(之后会看到)确定;
[id]:webpack给块分配的内部chunk id,如果你没有隐藏,你能在打包后的命令行中看到;
[hash]:每次构建过程中,生成的唯一 hash 值;
[chunkhash]: 依据于打包生成文件内容的 hash 值,内容不变,值不变;
[ext]: 资源扩展名,如js,jsx,png等等;
对webpack而言,所有的文件都是模块,文件可以当做模块,而模块却不一定是一个独立的文件。
loader用来识别特定文件供webpack进行转换后输出文件:babel-loader postcss-loader file-loader url-loader style-loader css-loader sass-loader vue-loader
plugin是补充一些webpack本来没有功能插件:HtmlWebpackPlugin、 cleanWebpackPlugin打包自动删除上次打包文件、静态资源拷贝copy-webpack-plugin插件
首页往往不需用到主js文件的所有代码,实际开发中我们常常使用一定方法对代码进行分割,方便按需加载,提升体验。这类不具备独立依赖的文件,我们称之为chunkfile。chunkfile的命名,在output中对应chunkFilename项。
通常,chunk 直接与 bundle 相对应,但是有些配置不会产生一对一的关系。
chunk(代码块)是一些模块 (module) 的封装单元。于 webpack 运行时的 seal 封包阶段生成,且直到资源构建阶段都会持续发生变化的代码块,在此期间插件通过各种钩子事件侵入各个编译阶段对 chunk 进行优化处理.
webpack4以后的版本支持零配置的方式直接启动打包,修改配置则在项目根目录下新建webpack.config.js文件作为webpack配置文件;
webpack.config.js运行在node.js环境,需要以commonJS的方式导入文件,导出成员,可以使用node.js内置模块
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
module.exports = {
//devtool :此处的配置值为cheap-module-source-map,代表不带列映射的 SourceMap,将加载的 Source Map 简化为每行单独映射。
devtool: 'cheap-module-source-map',
//此处的entry是一个数组,代表着四项的代码都会添加到打包结果之中
entry: [
require.resolve('react-dev-utils/webpackHotDevClient'), //webpackHotDevClient可以被看做具有更好体验的WebpackDevServer。
require.resolve('./polyfills'),//./ployfill.js用以在浏览器中支持 promise/fetch/object-assign。
require.resolve('react-error-overlay'),//react-error-overlay在开发环境中使用,强制显示错误页面。
'src/index.js' //我们的app的主入口
],
output: {
path: '/build/',//path指定打包后文件存放的位置为/build/。
pathinfo: true,//打包文件后在其中包含引用模块的信息,开发环境中有利于调试
filename: 'static/js/bundle.js',//指定了打包的名字和基本的引用路径
chunkFilename: 'static/js/[name].chunk.js',//指定了非入口文件的名称
publicPath: '',//指定服务器读取时的路径
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), //这里是一个函数,指定了map位于磁盘的位置
},
resolve: {
//modules指定了模块的搜索的位置,这里设置为node_modules
modules: ['node_modules'],
//extensions指明在引用模块时哪些后缀名可以忽略,这里忽略的文件名包括.js/.jsx/.web.js/.web.jsx等
extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'],
//alias创建 import 或 require 的别名,使得部分模块的引用变得简单
alias: {
'react-native': 'react-native-web',
},
//plugins:此处使用了 ModuleScopePlugin 的实例,用以限制自己编写的模块只能从src目录中引入。
plugins: [new ModuleScopePlugin('/src')],
},
module: {
strictExportPresence: true,//设置为 true,表明文件中如果缺少 exports 时会直接报错而不是警告
rules: [
//对 js/jsx 文件前置使用 eslintFormatter
{
test: /\.(js|jsx)$/,
enforce: 'pre',
use: [{
options: {
formatter: eslintFormatter,
},
loader: require.resolve('eslint-loader'),
},],
include: 'src',
},
//对exclude中的众多文件类型不使用file-loader,并设置其它文件打包后的名称按'static/media/[name].[hash:8].[ext]'格式设置。
{
exclude: [/\.html$/, /\.(js|jsx)$/, /\.css$/, /\.json$/, /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
//对图片类型文件,调用url-loader进行处理
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// 对js/jsx文件调用babel-loader处理转换
{
test: /\.(js|jsx)$/,
include: 'src',
loader: require.resolve('babel-loader'),
options: {
cacheDirectory: true,
},
},
//对css文件,按顺序调用style-loader,css-loader,postcss-loader进行处理
{
test: /\.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
...
},
},
],
},],
},
plugins: [
//InterpolateHtmlPlugin和HtmlWebpackPlugin串行使用,允许在index.html中添加变量
new InterpolateHtmlPlugin({
NODE_ENV: 'development',
PUBLIC_URL: ''
}),
//HtmlWebpackPlugin自动生成带有入口文件引用的index.html
new HtmlWebpackPlugin({
inject: true,
template: 'public/index.html',
}),
//NamedModulesPlugin:当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境
new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({
'process.env': {
NODE_ENV: "development",
PUBLIC_URL: '" "'
}
}),
//HotModuleReplacementPlugin:启用模块热替换
new webpack.HotModuleReplacementPlugin(),
//CaseSensitivePathsPlugin:如果路径有误则直接报错。
new CaseSensitivePathsPlugin(),
//WatchMissingNodeModulesPlugin:此插件允许你安装库后自动重新构建打包文件
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),//忽略所匹配的moment.js
],
//设置node的dgram/fs/let/tls模块的的值,如果在其它环境中使用时值为empty
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
},
performance: {
hints: false,//不提示测试环境的打包结果
},
};
2.