We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在开始接触前端工程化的时候,第一个上手的工具就是webpack。最开始使用webpack,只是用在html+css+js的场景下进行打包配置。在这种场景下,项目工程的复杂度较低,基本上参照着webpack官网,配置好相关参数就可以跑通项目了。后来接触vue后,开始使用vue-cli脚手架简单搭建主体项目环境,也就只是稍微改动一些插件、loader以及其他配置。但是在接触到vue ssr后,逐渐意识到在webpack配置对前端开发的重要性,所以也就有了这篇笔记,重学webpack。
html+css+js
首先初始化项目:
npm init npm install webpack webpack-cli webpack-dev-server --save-dev
在执行完npm init后,项目目录下会多出一个package.json的文件,在这里可以看得到项目下安装的webpack插件,也修改npm命令。
npm init
package.json
补充说明: 在npm install安装相关工具时,如果使用了--save,依赖会添加到package.json中的dependencies选项中;如果使用了--save-dev,依赖会添加到package.json中的devDependencies中,注意区分。
npm install
--save
dependencies
--save-dev
devDependencies
在上述提到的插件中,webpack-dev-server可以提供一个简单的本地服务,并且具有热重载的功能。
webpack-dev-server
在项目路径下,新建一个名为webpack.config.js的配置文件,基本结构如下:
webpack.config.js
module.exports = { mode: 'development', entry: ['./src/index.js'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js' //如果担心缓存影响热更新效果,可以使用[name].[hash:8].js }, externals: {}, devtool: 'eval', resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }, module: { rules: [] }, plugins: [], optimization: {}, devServer: {} }
简要说明一下该配置文件的内容:
其中,开发环境推荐使用cheap-module-eval-source-map,生产环境推荐: cheap-module-source-map(这也是下版本 webpack 使用-d命令启动debug模式时的默认选项)
cheap-module-eval-source-map
cheap-module-source-map
其他的具体配置参数可以参考:webpack官方文档 - devtool以及深入浅出的webpack构建工具---devTool中SourceMap模式详解(四)
./src/index.js
./dist/bundle.js
path
external: { jquery: 'jQuery' }
具体可参考:外部扩展(externals) 另外补充一点,如果需要全局使用jQuery,那么可以在webpack公共配置文件(webpack.base.conf.js)中设置,其中ProvidePlugin提供和暴露全局变量:
ProvidePlugin
module.exports = { plugins: [ new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }) ] }
假如我们要单独打包类似jQuery这样的类库,可以在公共配置中加上设置如下:
module.exports = { plugins: [], optmization: { cacheGroup: { commons: { test: /jquery/, name: 'jquery', chunks: 'all' } } } }
另外的一些概念:
在node当中,process表示的是当前node的进程,process.env就包括了系统环境信息,但是process.env不包括NODE_ENV这个属性,她是用户自定义的变量,用来判断当前环境。 而我们在package.json配置中就有关于环境变量NODE_ENV的配置(当然你放在其他文件下也没问题),如:
{ 'script': { 'dev': "NODE_ENV=development webpack --config webpack.dev.conf.js" } }
这样,我们就将NODE_ENV绑定到了process.env上,并且按以上配置,我们只能在webpack.dev.conf.js中以及它所引入的脚本中访问到process.env.NODE_ENV,其他地方都不可访问到。
webpack的构建流程是串行的,如下:
Compiler
流程图可以参考这里:https://juejin.im/post/5c6b78cdf265da2da15db125
webpack打包的规则是,一个入口文件对应一个bundle,该bundle包括了入口文件模块和其他依赖模块,按需加载的模块或者需要单独加载的模块则是分开打包生成其他的bundle。在这些bundle中,有一个较为特殊,就是manifest.bundle.js,被称作webpackBootstrap,它是最先加载的,负责解析webpack生成的其他bundle。
安装:
npm i babel-loader babel-core babel-preset-env babel/plugin-transform-runtime --save-dev
babel是JavaScript的转码器,可以现代js脚本内容转换为ES5、ES3。下面介绍几个常用的babel loader:
补充说明babel-stage-x对应的ECMA标准:
在webpack配置对应的babel loader时,也需要在项目下创建.babelrc文件,下面举出demo:
.babelrc
// vue-cli的配置: { "presets": [ "es2015", "stage-3" ], "plugins": [ "syntax-dynamic-import" ] } // react配置可以参考: { "presets": ["es2015", "stage-2", "react"], "plugins": [ "react-hot-loader/babel", "transform-function-bind", "transform-class-properties", "transform-export-extensions", ], "env": { "backend": { "plugins": [ [ "webpack-loaders", { "config": "./webpack.config.babel.js" , "verbose": true } ] ] } } } }
webpack的配置举例如下:
module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, user: { loader: 'babel-loader?cacheDirectory=true', // cacheDirectory用于缓存babel的编译结果,加快重新编译的速度 options: { presets: ['@babel/preset-env'], plugins: [ '@babel/plughin-transform-runtime', '@babel/plugin-transform-modules-commonjs' ] } } } ] } }
常见的css预处理器有less、sass、stylus等,它们都有对应的loader,以less和css的配置为例:
rules: [ { test: /\.less$/, exclude: /node_modules/, use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'less-loader', 'postcss-loader'] }, { test: /\.scss$/, use: [ "style-loader", // 将 JS 字符串生成为 style 节点 "css-loader", // �将 CSS 转化成 CommonJS 模块 "sass-loader" // 将 Sass 编译成 CSS,默认使用 Node Sass ] }, { test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] } ] // postcss.config.js module.exports = { plugins: [ require('autoprefixer') ] }
如果我们要处理一些图片,就很大概率上会接触到file-loader和url-loader,二者有相似之处,二者在处理图片时可以将其打包到dist目录,接着会获取图片模块的地址,并将地址返回到引入模块的变量中。url-loader基本可以实现file-loader的功能,但是在url-loader是将图片转换为base64直接放入bundle.js下,而file-loader会将图片放到dist目录下。
rules: [ { test: /\.(png|jpg|gif|jpeg)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', // placeholder 占位符 outputPath: 'images/', // 打包文件名 limit: 204800, // 小于200kb则打包到js文件里,大于则使用file-loader的打包方式打包到imgages里 }, }, }, { test: /\.(eot|woff2?|ttf|svg)$/, use: { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', // 和上面同理 outputPath: 'fonts/', limit: 5000, } }, } ]
webpack.base.conf.js
webpack.dev.conf.js
webpack.prod.conf.js
const path = require('path'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const HtmlWebpackPlugin = reuqire('html-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { // 多入口文件打包 entry: { index: './src/js/index.js', login: './src/js/login.js' }, optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] }, plugins: [ new webpack.HotModuleReplacementPlugin(), // 开启全局的模块热替换(HMR) new webpack.NamedModulesPlugin(), // 当模块热替换(HMR)时在浏览器控制台输出对用户更友好的模块名字信息 new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development') }), new CleanWebpackPlugin(), // 以下两个热重载插件 new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin(), new ExtractTextPlugin('css/[name].[hash].css'), // 多页打包 new HtmlWebpackPlugin({ filename: 'index.html', template: './src/index.html', chunks: ['index'] }), new HtmlWebpackPlugin({ filename: 'login.html', template: './src/login.html', chunks: ['login'] }), new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: devMode ? '[name].css' : '[name].[hash].css', chunkFilename: devMode ? '[id].css' : '[id].[hash].css', }), new CSSSplitWebpackPlugin({ size: 4000, filename: '[name]-[part].[ext]' }) ], module: { noParse:/jquery|lodash/, // 对于没有使用require和import,而是通过CDN引用的模块,不需要使用webpack进行解析依赖 rules: [ { test: /\.(sa|sc|c)ss$/, use: [ devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader', ], } ] }, output: { filename: 'js/[name].js', path: path.resolve(__dirname, 'dist') } }
可以通过resolve.modules:[path.resolve(__dirname, 'node_modules')]来定位第三方模块,默认是node_modules;设置resolve.alias来生成一个快速引用符号指向目标目录;在使用loader的时候,可以通过test、exclude、include、正则来缩小搜索范围
resolve.modules:[path.resolve(__dirname, 'node_modules')]
node_modules
resolve.alias
另外,使用alias可以加快webpack查找模块的速度。
alias
resolve: { extensions: ['.js', '.css', '.vue'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), }, mainFields: ['style', 'main'], modules:[path.resolve(__dirname, node_modules), my_modules], //只能引入node_modules和mu_modules下的包 mianFiles:['index.js','main.js'] }
如果在两个组件中都引用了共同的组件,那么我们可以将公共组件代码抽离出来
const path = require('path'); module.export = { entry: { index: './src/index', login: './src/login' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, optimization: { splitChunks: { cacheGroups: { common: { chunks: 'initial', minChunks: 2, // 使用两个同时引用的模块时才抽离出来 minSize: 0 // 限制大小,小于指定值就不抽离 } } } } }
最后打包的结果中会生成index.js和login.js
index.js
login.js
或者是:
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); plugins:[ new CommonsChunkPlugin({ chunks:['chunkA','chunkB'], //从哪些chunk中提取 name:'common', // 提取出的公共部分生成一个新的chunk,文件为common.js }) ]
DllPlugin是动态链接库插件,原理是将网页依赖的基础模块抽离出来打包到dll文件中,当需要导入的模块存在于某个dll文件中,该模块不再被打包,而是通过dll获取。
// webpack.dll.config.js const path = require('path'); const webpack = require('webpack'); module.exports ={ entry: { vendor: ['react', 'redux', 'react-router'], }, output: { path: path.join(__dirname, 'dist'), filename: '[name].dll.js', library: '[name]_[hash]' //提供全局的变量 }, plugins: [ new webpack.DllPlugin({ path: path.join(__dirname, 'dist', '[name].manifest.json'), name: '[name]_[hash]', }), ], }; // package.json "scripts": { "dev": "webpack-dev-server", "build": "webpack", "dll":"webpack --config webpack.dll.config.js" }, // webpack.base.conf.js const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname,'dist'), filename: 'index_bundle.js', }, plugins: [ new webpack.DllReferencePlugin({ context: path.join(__dirname), manifest:path.resolve(__dirname,'dist','vendor.manifest.json') }), new HtmlWebpackPlugin(), new AddAssetHtmlPlugin({ filepath: path.resolve(__dirname,'dist','vendor.manifest.json') }), ], }; 作者:梦想攻城狮 链接:https://juejin.im/post/5bd128b56fb9a05ce1729333 来源:掘金
打包后生成:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Webpack App</title> </head> <body> <script type="text/javascript" src="vendor-manifest.json"></script> <script type="text/javascript" src="index_bundle.js"></script> </body> </html>
IgnorePlugin可以让webpack不打包指定的模块:
module.exports = { ... plugins: [ new webpack.IgnorePlugin(/moment$/) ] ... }
const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { plugins: [ new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] } ]) ] }
webpack内置的uglifyjs插件可以实现js代码压缩:
const UglifyJSPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); //... plugins: [ new UglifyJSPlugin({ compress: { warnings: false, //删除无用代码时不输出警告 drop_console: true, //删除所有console语句,可以兼容IE collapse_vars: true, //内嵌已定义但只使用一次的变量 reduce_vars: true, //提取使用多次但没定义的静态值到变量 }, output: { beautify: false, //最紧凑的输出,不保留空格和制表符 comments: false, //删除所有注释 } }) ] 作者:superMaryyy 链接:https://juejin.im/post/5b652b036fb9a04fa01d616b 来源:掘金
另外,使用css-loader?minimize不仅可以删除样式文件的空格,还可以语义化地压缩css代码(rgb转色彩名)
css-loader?minimize
参考:
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const {WebPlugin} = require('web-webpack-plugin'); //... output:{ filename: '[name]_[chunkhash:8].js', path: path.resolve(__dirname, 'dist'), publicPatch: '//js.cdn.com/id/', //指定存放JS文件的CDN地址 }, module:{ rules:[{ test: /\.css/, use: ExtractTextPlugin.extract({ use: ['css-loader?minimize'], publicPatch: '//img.cdn.com/id/', //指定css文件中导入的图片等资源存放的cdn地址 }), },{ test: /\.png/, use: ['file-loader?name=[name]_[hash:8].[ext]'], //为输出的PNG文件名加上Hash值 }] }, plugins:[ new WebPlugin({ template: './template.html', filename: 'index.html', stylePublicPath: '//css.cdn.com/id/', //指定存放CSS文件的CDN地址 }), new ExtractTextPlugin({ filename:`[name]_[contenthash:8].css`, //为输出的CSS文件加上Hash }) ] 作者:superMaryyy 链接:https://juejin.im/post/5b652b036fb9a04fa01d616b 来源:掘金
可以使用HappyPack这个插件进行webpack的多进程打包,以下放出网上找来的一段相关配置,来自:加速 Webpack,文章内也有具体的配置说明,详情可点击链接访问
HappyPack
const path = require('path'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HappyPack = require('happypack'); module.exports = { module: { rules: [ { test: /\.js$/, // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例 use:['happypack/loader?id=babel'], // 排除 node_modules 目录下的文件,node_modules目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换 exclude: path.resolve(__dirname, 'node_modules'), }, { // 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例 test: /\.css$/, use:ExtractTextPlugin.extract({ use: ['happypack/loader?id=css'], }), }, ] }, plugins: [ new HappyPack({ // 用唯一的标识符 id 来代表当前的HappyPack 是用来处理一类特定的文件 id: 'babel', // 如何处理 .js 文件,用法和 Loader配置中一样 loaders: ['babel-loader?cacheDirectory'], }), new HappyPack({ id: 'css', // 如何处理 .css 文件,用法和Loader 配置中一样 loaders: ['css-loader'], }), new ExtractTextPlugin({ filename: `[name].css`, }), ], };
在webpack项目构建中,有三种基本的代码类型:
在单页应用中,客户端发起网络请求后,从服务端返回的文件主要是入口的html文件和一系列bundle文件,然后交由浏览器解析处理。经由webpack打包过后的项目,在不同的打包策略模式下,代码的执行逻辑是不同的,但是它们都需要通过webpack来进行管理模块间的交互。而runtime和manifest就是指在浏览器运行时,webpack通过连接模块化应用程序的所有代码。runtime包括了在模块交互时,连接模块所需的加载和解析逻辑,包括浏览器中已加载模块的连接、懒加载模块的执行逻辑。
为了更加清晰地理解manifest,我们简要复述一遍webpack的工作流程:
当编译器(compiler)开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 "Manifest",当完成打包并发送到浏览器时,会在运行时通过 Manifest 来解析和加载模块。无论你选择哪种模块语法,那些 import 或 require 语句现在都已经转换为 __webpack_require__ 方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够查询模块标识符,检索出背后对应的模块。 -- webpack官方文档
当webpack写入bundle时,它会维护一个manifest,你可以在项目中生成的bundle找到它,它内部描述了webpack应该加载的文件。如果文件的hash值改变,manifest也会改变,然后也跟着一些分离出来的代码共同打包。这显然不是我们想要的,所以我们需要提取出manifest:
new webpack.optimize.CommonsChunkPlugin({ name: 'manifest' // 用于提取manifest })
浏览器加载js文件时,如果文件名和浏览器本地缓存一致,那么就不会向后台发送请求。假如我们的业务代码中做了修改,但是webpack打包出来的文件名对应的hash值不变,那么就不会用上新代码。另外,在前端项目中一般是只有业务代码才会频繁变化,而第三方依赖是很少变化的,所以我们希望浏览器不用再重复地向服务端发送请求,这就要求我们进行代码分块打包,将一些第三方依赖集中整合进单个bundle下。所以,我们可以借助webpack.optimize.CommonsChunkPlugin将manifest、第三方依赖、业务代码分别独立打包
可以使用webpack-bundle-analyzer对webpack打包结果进行分析,支持可视化的。
webpack-bundle-analyzer
module.exports = { entry: './index.js', output: { filename: 'bundle[chunkhash:8].js', path: path.resolve(__dirname, 'dist') }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module, count) { // 将node_modules的依赖集中打包,注意主次关系,依赖要放在manifest前面 return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // 抽出mainfest new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) ] }
The text was updated successfully, but these errors were encountered:
在工程中如果代码混用了这两种规范,可能就遇到构建失败的情况。实际上,这样的方式也是不提倡的。可以参考下面的讨论:
webpack/webpack#4039 (comment)
事实上,webpack内部就维护了包管理工具,支持ES Module、Commonjs、AMD 模块化规范。 如果需要处理这样的冲突问题,可以对babel的配置文件进行修改,将modules的配置选项设置为commonjs(js最终编译得到CommonJS规范)或者umd(js最终编译得到umd规范)以支持这两种模块规范的混用。
modules
commonjs
umd
Sorry, something went wrong.
No branches or pull requests
重学webpack
在开始接触前端工程化的时候,第一个上手的工具就是webpack。最开始使用webpack,只是用在
html+css+js
的场景下进行打包配置。在这种场景下,项目工程的复杂度较低,基本上参照着webpack官网,配置好相关参数就可以跑通项目了。后来接触vue后,开始使用vue-cli脚手架简单搭建主体项目环境,也就只是稍微改动一些插件、loader以及其他配置。但是在接触到vue ssr后,逐渐意识到在webpack配置对前端开发的重要性,所以也就有了这篇笔记,重学webpack。1 初始化项目
1.1 安装依赖
首先初始化项目:
在执行完
npm init
后,项目目录下会多出一个package.json
的文件,在这里可以看得到项目下安装的webpack插件,也修改npm命令。补充说明:
在
npm install
安装相关工具时,如果使用了--save
,依赖会添加到package.json
中的dependencies
选项中;如果使用了--save-dev
,依赖会添加到package.json
中的devDependencies
中,注意区分。在上述提到的插件中,
webpack-dev-server
可以提供一个简单的本地服务,并且具有热重载的功能。1.2 编辑配置
在项目路径下,新建一个名为
webpack.config.js
的配置文件,基本结构如下:简要说明一下该配置文件的内容:
其中,开发环境推荐使用
cheap-module-eval-source-map
,生产环境推荐:cheap-module-source-map
(这也是下版本 webpack 使用-d命令启动debug模式时的默认选项)其他的具体配置参数可以参考:webpack官方文档 - devtool以及深入浅出的webpack构建工具---devTool中SourceMap模式详解(四)
./src/index.js
打包为./dist/bundle.js
。其中,这里有几个关键的path
变量信息,可参考:webpack中的path、publicPath和contentBase具体可参考:外部扩展(externals)
另外补充一点,如果需要全局使用jQuery,那么可以在webpack公共配置文件(webpack.base.conf.js)中设置,其中
ProvidePlugin
提供和暴露全局变量:假如我们要单独打包类似jQuery这样的类库,可以在公共配置中加上设置如下:
另外的一些概念:
关于webpack配置中的process.env
在node当中,process表示的是当前node的进程,process.env就包括了系统环境信息,但是process.env不包括NODE_ENV这个属性,她是用户自定义的变量,用来判断当前环境。
而我们在
package.json
配置中就有关于环境变量NODE_ENV的配置(当然你放在其他文件下也没问题),如:这样,我们就将NODE_ENV绑定到了process.env上,并且按以上配置,我们只能在webpack.dev.conf.js中以及它所引入的脚本中访问到process.env.NODE_ENV,其他地方都不可访问到。
2 构建流程
webpack的构建流程是串行的,如下:
package.json
读取对应的命令配置,得出最终参数Compiler
,加载所有plugin,执行对象的run方法并开始编译。接着根据配置中的entry找到所有入口文件流程图可以参考这里:https://juejin.im/post/5c6b78cdf265da2da15db125
webpack打包的规则是,一个入口文件对应一个bundle,该bundle包括了入口文件模块和其他依赖模块,按需加载的模块或者需要单独加载的模块则是分开打包生成其他的bundle。在这些bundle中,有一个较为特殊,就是manifest.bundle.js,被称作webpackBootstrap,它是最先加载的,负责解析webpack生成的其他bundle。
3.具体配置
3.1 loader
3.1.1 babel
安装:
babel是JavaScript的转码器,可以现代js脚本内容转换为ES5、ES3。下面介绍几个常用的babel loader:
补充说明babel-stage-x对应的ECMA标准:
stage标准是向下兼容的,比如stage0包括了stage1、stage2、stage3、stage4的内容。
可以参考:What's the difference between babel-preset-stage-0, babel-preset-stage-1 etc?
在webpack配置对应的babel loader时,也需要在项目下创建
.babelrc
文件,下面举出demo:webpack的配置举例如下:
3.1.2 样式相关的loader
常见的css预处理器有less、sass、stylus等,它们都有对应的loader,以less和css的配置为例:
3.1.3 静态资源相关loader
如果我们要处理一些图片,就很大概率上会接触到file-loader和url-loader,二者有相似之处,二者在处理图片时可以将其打包到dist目录,接着会获取图片模块的地址,并将地址返回到引入模块的变量中。url-loader基本可以实现file-loader的功能,但是在url-loader是将图片转换为base64直接放入bundle.js下,而file-loader会将图片放到dist目录下。
3.2 plugin
3.2.1 常用插件
webpack.base.conf.js
(公共配置)、webpack.dev.conf.js
(开发环境配置)、webpack.prod.conf.js
(生产环境配置)的配置文件。然后再合并webpack配置项,比如合并webpack.base.conf.js
和webpack.dev.conf.js
4 优化
4.1 巧用resolve
可以通过
resolve.modules:[path.resolve(__dirname, 'node_modules')]
来定位第三方模块,默认是node_modules
;设置resolve.alias
来生成一个快速引用符号指向目标目录;在使用loader的时候,可以通过test、exclude、include、正则来缩小搜索范围另外,使用
alias
可以加快webpack查找模块的速度。4.2 抽离公共代码
如果在两个组件中都引用了共同的组件,那么我们可以将公共组件代码抽离出来
最后打包的结果中会生成
index.js
和login.js
或者是:
4.3 使用DllPlugin减少基础模块的编译次数
DllPlugin是动态链接库插件,原理是将网页依赖的基础模块抽离出来打包到dll文件中,当需要导入的模块存在于某个dll文件中,该模块不再被打包,而是通过dll获取。
打包后生成:
4.4 使用IgnorePlugin忽略某些模块打包
IgnorePlugin可以让webpack不打包指定的模块:
4.5 生产环境中将提前构建的包同步到dist
4.6 代码压缩
webpack内置的uglifyjs插件可以实现js代码压缩:
另外,使用
css-loader?minimize
不仅可以删除样式文件的空格,还可以语义化地压缩css代码(rgb转色彩名)4.7 使用静态资源CDN
参考:
4.8 多进程打包
可以使用
HappyPack
这个插件进行webpack的多进程打包,以下放出网上找来的一段相关配置,来自:加速 Webpack,文章内也有具体的配置说明,详情可点击链接访问4.9 runtime和manifest
在webpack项目构建中,有三种基本的代码类型:
在单页应用中,客户端发起网络请求后,从服务端返回的文件主要是入口的html文件和一系列bundle文件,然后交由浏览器解析处理。经由webpack打包过后的项目,在不同的打包策略模式下,代码的执行逻辑是不同的,但是它们都需要通过webpack来进行管理模块间的交互。而runtime和manifest就是指在浏览器运行时,webpack通过连接模块化应用程序的所有代码。runtime包括了在模块交互时,连接模块所需的加载和解析逻辑,包括浏览器中已加载模块的连接、懒加载模块的执行逻辑。
为了更加清晰地理解manifest,我们简要复述一遍webpack的工作流程:
当webpack写入bundle时,它会维护一个manifest,你可以在项目中生成的bundle找到它,它内部描述了webpack应该加载的文件。如果文件的hash值改变,manifest也会改变,然后也跟着一些分离出来的代码共同打包。这显然不是我们想要的,所以我们需要提取出manifest:
4.10 缓存优化
浏览器加载js文件时,如果文件名和浏览器本地缓存一致,那么就不会向后台发送请求。假如我们的业务代码中做了修改,但是webpack打包出来的文件名对应的hash值不变,那么就不会用上新代码。另外,在前端项目中一般是只有业务代码才会频繁变化,而第三方依赖是很少变化的,所以我们希望浏览器不用再重复地向服务端发送请求,这就要求我们进行代码分块打包,将一些第三方依赖集中整合进单个bundle下。所以,我们可以借助webpack.optimize.CommonsChunkPlugin将manifest、第三方依赖、业务代码分别独立打包
4.11 打包分析
可以使用
webpack-bundle-analyzer
对webpack打包结果进行分析,支持可视化的。参考:
扩展阅读:
The text was updated successfully, but these errors were encountered: