关于webpack
webpakc的是模块打包器.而不是任务执行器。
它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
webpack也提供了便捷的打包流程,项目构建,插件管理等等.
为更好的构建项目,从开发到生产都一一提供了解决方案.
Vue官方也推荐使用的vue-loader也是基于webpack的.
Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。
Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
Webpack的处理速度更快更直接,能打包更多不同类型的文件。
一个常见的webpack配置文件
webpack.config.js
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‘ }, output: { //publicPath:XXX 可以资金定义路径,build后输出的内容将会放入到这个路径当中 filename: ‘[name]-[hash].js‘, //打包后输出文件的文件名,注意name用方括号括起来,name的值对应的为entry的key值,这样为了区分多个文件 path: path.resolve(__dirname, ‘dist‘)//打包后的文件存放的地方(__dirname指向当前执行脚本所在的目录) }, devtool: ‘eval-source-map‘,//Source Maps提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试,强调你只应该开发阶段使用它 devServer: {//本地开发服务器,浏览器监听你的代码的修改,并自动刷新显示修改后的结果 contentBase: ‘./dist‘,//本地服务器所加载的页面所在的目录 historyApiFallback: true, //不跳转,所有的跳转将指向index.html inline: true,//实时刷新 hot: true }, module: {//Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置 rules: [ { test: /\.css$/,//一个用以匹配loaders所处理文件的拓展名的正则表达式(必须) use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true, //通过CSS模块,所有的类名,动画名默认都只作用于当前模块。 指定启用css modules,把CSS的类名传递到组件的代码中,这样做有效避免了全局污染 localIdentName: ‘[name]__[local]--[hash:base64:5]‘ // 指定css的类名格式 } ]//css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader插件使对于CSS文件进行实时渲染到页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。 }, { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader",//loader的名称(必须) options: {//为loaders提供额外的设置选项(可选) presets: [ "env", "react"//解析Es6的babel-env-preset包和解析JSX的babel-preset-react包 ] } }, exclude: /node_modules/ //include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选); }, { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [{ loader: "css-loader", options: { modules: true, localIdentName: ‘[name]__[local]--[hash:base64:5]‘ } }, { loader: "postcss-loader"//自动添加适应不同浏览器的css前缀 }], }) } ] }, plugins: [ new CleanWebpackPlugin([‘dist‘]),//清除dist目录 new HtmlWebpackPlugin({ //依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。 title: ‘Hot Module Replacement‘ }), new webpack.NamedModulesPlugin(),//添加该插件更容易观察依赖文件被更新 new webpack.HotModuleReplacementPlugin(),//动态替换文件,热加载插件 new webpack.BannerPlugin(‘版权所有,翻版必究‘),//版本申请插件 new webpack.optimize.CommonsChunkPlugin({ name: ‘common‘ // Specify the common bundle‘s name. }), new webpack.HashedModuleIdsPlugin(),//为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID new webpack.optimize.OccurrenceOrderPlugin(),// new webpack.optimize.UglifyJsPlugin(),//压缩JS代码 new ExtractTextPlugin("style.css")//分离CSS和JS文件 ], };
package.json
{ "name": "webpack demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack", "server": "webpack-dev-server --open", "build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress" }, "author": "", "license": "ISC","dependencies": {
"swiper": "^4.0.7",
"vue": "^2.5.2",
"vue-router": "^2.8.1",
"element-ui": "2.2.1",
"react": "^15.6.1", "react-dom": "^15.6.1" } }
使用webpack
1. 安装
全局安装:
//全局安装 npm install -g webpack
局部安装:
//局部安装 npm install --save-dev webpack
2.工程结构
可以根据项目的实际的情况设置目录。
一般情况下可以设置为源码目录,生产目录。
目录结构如下:
3. 写一个webpack.config.js配置文件,首先是入口文件路径(entry)和打包后文件的存放路径(output)。
module.exports = { entry: __dirname + "/src/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/dist",//打包后的文件存放的地方 filename: "bundle.js"//打包后输出文件的文件名 }
4. 对npm
进行配置后可以在命令行中使用简单的npm start
命令来执行打包任务。在package.json
中对scripts
对象进行相关设置即可,package.json
中的script
会按照一定顺序寻找命令对应位置,具体设置方法如下:
{ "name": "webpack-sample-project", "version": "1.0.0", "description": "Sample webpack project", "scripts": { "start": "webpack" }, "author": "zhang", "license": "ISC", "devDependencies": { "webpack": "3.10.0" } }
注意:
npm的start
命令是一个特殊的脚本名称,其特殊性表现在,在命令行中使用npm start
就可以执行其对于的命令。如果对应的此脚本名称不是start
,想要在命令行中运行时,需要这样用npm run {script name},
如npm run build
5. 开发工具
webpack 提供了强大的开发工具
webpack-server 提供了访问页面的服务
webpack-watch 提供了观察文件的变化
1> source maps
开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的。而webpack
可以在打包时为我们生成source maps
,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。
在webpack
的配置文件中配置source maps
,需要配置devtool。
对小到中型的项目中,eval-source-map
是一个很好的选项,再次强调,你只应该开发阶段使用它。
module.exports = { devtool: ‘eval-source-map‘,//source maps,便于调试 entry: __dirname + "/src/main.js", output: { path: __dirname + "/dist", filename: "bundle.js" } }
2>使用webpack构建本地服务器
让你的浏览器监听你的代码的修改,并自动刷新显示修改后的结果,其实Webpack
提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖。
npm install --save-dev webpack-dev-server
webpack.config.js配置文件如下:
module.exports = { devtool: ‘eval-source-map‘, entry: __dirname + "/src/main.js", output: { path: __dirname + "/dist", filename: "bundle.js" }, devServer: { contentBase: "./dist",//本地服务器所加载的页面所在的目录 historyApiFallback: true,//不跳转 inline: true//实时刷新 } }
在package.json
中的scripts
对象中添加如下命令,用以开启本地服务器:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack", "server": "webpack-dev-server --open" },
在终端中输入npm run server,
即可在本地的8080
端口查看结果
6. Loaders
Loaders
是webpack
提供的最激动人心的功能之一。通过使用不同的loader
,webpack
有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。webpack.config.js
中的modules
关键字下进行配置。test
:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)loader
:loader的名称(必须)include/exclude
:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);query
:为loaders提供额外的设置选项(可选)css-loader
和 style-loader
,二者处理的任务不同。css-loader
使你能够使用类似@import
和 url(...)
的方法实现 require()
的功能;style-loader
将所有的计算后的样式加入页面中;安装style-oader、css-loader,loader的作用就是将文件进行处理(编译,压缩)
npm install --save-dev style-loader css-loader
module.exports = { module: { rules: [ { test: /\.css$/, //指定匹配文件,使用style-loader,css-loader use: [ ‘style-loader‘, ‘css-loader‘ ] }] } }
css预处理器
Sass
和 Less
之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables
, nesting
, mixins
, inheritance
等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句。
以下是常用的CSS 处理loaders
:
Less Loader
Sass Loader
Stylus Loader
-PostCSS
使用PostCSS来为CSS代码自动添加适应不同浏览器的CSS前缀。
首先安装postcss-loader
和 autoprefixer
(自动添加前缀的插件)
npm install --save-dev postcss-loader autoprefixer
在webpack配置文件中添加postcss-loader
,在根目录新建postcss.config.js。
//webpack.config.js module.exports = { ... module: { rules: [ { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] } }
postcss.config.js
module.exports = { plugins: [ require(‘autoprefixer‘) ] }
2> babel
Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:
先来一次性安装依赖包
// npm一次性安装多个依赖模块,模块之间用空格隔开 npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
在webpack
中配置Babel的方法如下:
module.exports = { ... module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader", options: { presets: [ "env", "react" ] } }, exclude: /node_modules/ } ] } };
3> Vue-Loader
.vue
文件是一个自定义的文件类型,用类 HTML 语法描述一个 Vue 组件。每个 .vue
文件包含三种类型的顶级语言块 <template>
、<script>
和 <style>
,还允许添加可选的自定义块:
<template> <div class="example">{{ msg }}</div> </template> <script> export default { data () { return { msg: ‘Hello world!‘ } } } </script> <style> .example { color: red; } </style> <custom1> This could be e.g. documentation for the component. </custom1>
vue-loader
会解析文件,提取每个语言块,如有必要会通过其它 loader 处理,最后将他们组装成一个 CommonJS 模块,module.exports
出一个 Vue.js 组件对象。
vue-loader
支持使用非默认语言,比如 CSS 预处理器,预编译的 HTML 模版语言,通过设置语言块的 lang
属性。例如,你可以像下面这样使用 Sass 语法编写样式:
<style lang="sass"> /* write Sass! */ </style>
7. 插件(Plugins)
插件(Plugins)用来拓展webpack功能,他们会在整个构建过程中生效,执行相关的任务。
loaders是在打包构建过程中用来处理源文件的(JSX,Scss, Less),一次处理一个。
plugins并不直接操作单个文件,是对整个构建过程起作用。
使用插件的方法:
使用某个插件,需要在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)。
例如:添加了一个给打包后代码添加版本声明的插件
const webpack = require(‘webpack‘); module.exports = { ... module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] }, plugins: [ new webpack.BannerPlugin(‘版权所有,翻版必究‘) ], };
下面推荐几个常用的插件:
依据一个简单的index.html
模板,生成一个自动引用你打包后的JS文件的新index.html
。这在每次生成的js文件名称不同时非常有用(比如添加了hash
值)。
安装:
npm install --save-dev html-webpack-plugin
webpack.config.js:
const webpack = require(‘webpack‘); const HtmlWebpackPlugin = require(‘html-webpack-plugin‘); module.exports = { ... plugins: [ new webpack.BannerPlugin(‘版权所有,翻版必究‘), new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数 }) ], };
清除目录的插件。
添加了hash
之后,会导致改变文件内容后重新打包时,文件名不同而内容越来越多,因此这里介绍另外一个很好用的插件clean-webpack-plugin
。
引入clean-webpack-plugin
插件后在配置文件的plugins
中做相应配置即可:
安装:
npm install clean-webpack-plugin --save-dev
webpack.config.js:
整个配置文件,注意插件的配置顺序.
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘); const CleanWebpackPlugin = require(‘clean-webpack-plugin‘); module.exports = { ... plugins: [ new CleanWebpackPlugin([‘dist‘]),//清除dist目录 new HtmlWebpackPlugin({ //生成一个html页面指定其title,然后会自动将js文件生成引入js代码 title: ‘Output Management‘ }) ] };
主要是启动服务后文件可以进行热更新,例如css样式或者js等文件变更,会自动更新到页面上.只能用于开发环境不能用于生产环境.
它允许你在修改组件代码后,自动刷新实时预览修改后的效果。
在webpack中实现HMR也很简单,只需要做两项配置
webpack.config.js
const webpack = require(‘webpack‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
const CleanWebpackPlugin = require(‘clean-webpack-plugin‘);
module.exports = {
...
devtool: ‘eval-source-map‘,
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true,
hot: true
},
...
plugins: [
new webpack.BannerPlugin(‘版权所有,翻版必究‘),
new CleanWebpackPlugin([‘dist‘]),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
}),
new webpack.NamedModulesPlugin(),//添加该插件更容易观察依赖文件被更新
new webpack.HotModuleReplacementPlugin()//热加载插件
],
};
目前为止,我们已经使用webpack构建了一个完整的开发环境。
产品阶段的构建
在产品阶段,可能还需要对打包的文件进行额外的处理,比如说优化,压缩,缓存以及分离CSS和JS。
对于复杂的项目来说,需要复杂的配置,这时候分解配置文件为多个小的文件可以使得事情井井有条。因此最好添加如下配置到配置文件的入口.
process.env.NODE_ENV = ‘production‘
webpack 配置可以提炼出来,使用webpack-merge
将webpack的配置文件进行合并。
我们可以建一个webpack.common.js
文件,然后创建一个webpack.prod.js
,webpack.dev.js
,那么可以将prod,dev引入common共同引用参数.
common文件
const path = require(‘path‘); const CleanWebpackPlugin = require(‘clean-webpack-plugin‘); const HtmlWebpackPlugin = require(‘html-webpack-plugin‘); module.exports = { entry: { app: ‘./src/index.js‘ }, plugins: [ new CleanWebpackPlugin([‘dist‘]), new HtmlWebpackPlugin({ title: ‘Production‘ }) ], output: { filename: ‘[name].bundle.js‘, path: path.resolve(__dirname, ‘dist‘) } };
生产环境:
webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能
OccurenceOrderPlugin
:为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的IDUglifyJsPlugin
:压缩JS代码;ExtractTextPlugin
:分离CSS和JS文件OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装其它非内置插件
npm install --save-dev extract-text-webpack-plugin
webpack.prod.js
const merge = require(‘webpack-merge‘); const UglifyJSPlugin = require(‘uglifyjs-webpack-plugin‘); const HtmlWebpackPlugin = require(‘html-webpack-plugin‘); const ExtractTextPlugin = require(‘extract-text-webpack-plugin‘); const common = require(‘./webpack.common.js‘); module.exports = merge(common, { module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] }, plugins: [ new webpack.BannerPlugin(‘版权所有,翻版必究‘), new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html" }), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin(), new ExtractTextPlugin("style.css") ] });
开发环境:
const merge = require(‘webpack-merge‘); const common = require(‘./webpack.common.js‘); module.exports = merge(common, { devtool: ‘inline-source-map‘, devServer: { contentBase: ‘./dist‘ } });
8. 代码管理
默认的情况下,webpack会把引用的文件都会build到单独的文件当中.我们可能会把一些第三方的模块放到一个单独文件里.
需要对配置文件做如下改动:
//plugins数组当中添加一下参数 new webpack.optimize.CommonsChunkPlugin({ name: ‘common‘ // Specify the common bundle‘s name. })
9. 垫片
当我们需要全局引入一个变量,我们可以使用ProvidePlugin。(webpack不推荐这样做,因为会影响到模块化编程)
每个js文件就不需要去import引入对应的代码
plugins: [ new webpack.ProvidePlugin({ _: ‘lodash‘ }) ]
或许只需要部分第三方库的功能,我们可以这样进行配置
plugins: [ new webpack.ProvidePlugin({ _: ‘lodash‘, join:[‘lodash‘, ‘join‘]//我们可以这样进行配置 [‘module‘, ‘child‘, ‘child‘] }) ]
10. 缓存
缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称相应改变)
webpack可以把一个哈希值添加到打包的文件名中,使用方法如下,添加特殊的字符串混合体([name], [id] and [hash])到输出文件名前。
module.exports = {
..
output: {
path: path.resolve(__dirname, ‘dist‘),
filename: "bundle-[hash].js"
},
...
};
还有个问题webpack默认的hash是根据module.id以及内容生成,而module.id根据解析文件的顺序生成,一个重要的问题是每次改动引入的文件,就可能会造成其它文件hahs不一致.
这时候就需要HashedModuleIdsPlugin插件,指定使用Path以及内容作为hash的内容.
只需要配置文件当中在plugins增加如下配置:
plugins:[ ..., new webpack.HashedModuleIdsPlugin(), ... ]
本文根据https://www.jianshu.com/p/42e11515c10f改写,加了一些自己的理解,希望对大家有帮助。
原文:https://www.cnblogs.com/Super-scarlett/p/11085293.html