本文webpack是在Mac平台下基于官方最新版本v3.10,对于webpack@v2会有小的差别,待全文完成后会补充webpack@v2与v3版本之间的差别
为了方便以后本身更好的使用这个webpack_starter,引入git的支持,一是能够把一些通用的东西放在主分支,二是能够把后面不一样的配置支持能够经过branch
或者tag
的方式分门别类。javascript
webpack_starter
项目,以下图所示,初始化.gitignore
支持Node
语言#clone项目到本地 git clone https://github.com/mpandar/webpack_starter.git cd webpack_starter
初始化过程按照提示完成便可,惟一注意的是entry point
,这是webpack进行打包时的入口文件,默认是根目录下的index.js
,不过一般状况下,咱们的源码都是在src
目录下,因此修改成src/index.js
> yarn init yarn init v1.3.2 question name (webpack_starter): question version (1.0.0): 0.1.0 question description: a webpack start project question entry point (index.js): src/index.js question repository url (https://github.com/mpandar/webpack_starter.git): question author (mpandar <mshp_****@126.com>): question license (MIT): question private: success Saved package.json ✨ Done in 53.55s.
单独安装与全局安装对于webpack的使用并没有太大差别,但推荐即便全局安装之后,仍要在项目中进行单独的安装,方便项目移植。不然可能会致使全局安装的webpack版本与项目中的配置文件可能存在不匹配。固然单独安装后,使用一些npm或yarn命令,它们会优先使用本地安装的webpack
#全局安装 yarn global add webpack #单项目使用 yarn add webpack
|--- |--dist //存放webpack打包后相关文件 |--src //存放项目源码 |--index.js |--config //项目相关的配置文件 |--webpack.config.js //webpack默认读取项目根目录下的webpack.config.js文件做为配置信息,为了规范化移入到config目录下 |--package.json
固然,即便没有配置文件,直接运行webpack命令,一样能够直接对js文件完成打包工做,这里是一篇分析webpack打包后的代码的文章:简要分析webpack打包后代码,其中用到的一个新命令npx,很简单,介绍点这里php
#直接打包 npx webpack src/index.js dist/bundle.js
为了应对更灵活的使用场景,webpack支持配置文件,而且默认状况下,在项目根目录下,若是存在webpack.config.js
文件,那么webpack会主动读取该文件做为配置内容,不过Demo下,为了更加符合咱们的目录规范,咱们将config文件移到了config
目录下css
//当配置文件内容为空时,运行该命令会提示`Configuration file found but no entry configured` npx webpack src/index.js --config config/webpack.config.js
接下来,让咱们看一下一个webpack配置文件,最简单只须要包含entry(定义入口文件)和output(定义打包输出文件)这两个部分:html
const path = require('path'); const base = path.join(__dirname, '..') module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') } };
注:使用path模块只是为了代码清晰,你彻底能够不用,直接用
__dirname+'/../src'
相似代码拼接
//这样就不须要在命令行定义输入输出文件啦 npx webpack --config config/webpack.config.js
除了entry
和output
,webpack中最多见的就是module
、resolve
、plugins
,大体结构以下:前端
module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') }, devtool: 'eval-source-map', devServer: { contentBase: path.resolve(base, 'dist'), historyApiFallback: true, inline: true, proxy: { "/api": "http://localhost:8000" } }, module: { rules: [ ] }, resolve: { }, plugins: [ ] };
固然了解webpack最好的地方永远是官方文档,传送门,接下来,天然是在目前配置的基础上,增添更多使人兴奋的特性java
开发离不开调试,但通过编码后的代码并不利于调试,很找到出错的地方对应的你写的代码,而Source Maps就是来帮咱们解决这个问题的。
而webpack支持Source Maps仅仅是增长一行 devtool
配置选项,具体配置选项能够看这里的官方文档,其中两个选项 eval-source-map
与 source-map
是比较经常使用的选项,前一个选项推荐仅仅用在开发环境,然后一个一般在一些第三方库中,提供给开发者调试使用。固然对于任何上线项目,实际上都推荐使用*.min.js并不使用Source Map以加快网络加载。node
module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') }, devtool: 'eval-source-map' }
做为开发者,总不但愿把时间浪费在执行命令和刷新页面上,webpack提供一个单独的组件webpack-dev-server
为咱们提供一个基于nodejs的本地服务器、文件修改监控及编译以及浏览器自动刷新等特性。webpack
首先是安装:git
yarn add webpack-dev-server --dev
详细配置参数可查阅官方文档,常使用参数以下:es6
module.exports = { entry: path.resolve(base, 'src', 'index.js'), output: { filename: 'bundle.js', path: path.resolve(base, 'dist') }, devtool: 'eval-source-map', devServer: { contentBase: path.resolve(base, 'dist'), historyApiFallback: true, inline: true, proxy: { "/api": "http://localhost:8000" } } }
小插曲,原本测试proxy的时候,本身利用php -S localhost:8000快速起了一个监听进程,可是访问前端的时候却提示转发去请求被拒绝,后来发现webpack-dev-server去转发请求的时候是把localhost转化为了地址,即127.0.0.1:8000,因此在php启动监听进程时候,须要使用php -S 127.0.0.1:8000
或者php -S 0.0.0.0:8000
监听全部网卡地址
webpack-dev-server的使用同webpack基本运行同样,只是webpack-dev-server是一个不会退出的进程,并自动监控文件变化等(Ctrl+C退出)
npx webpack-dev-server --config config/webpack.config.js
使用npx webpack --config config/webpack.config.js
进行打包实际上已经很方便了,可是当咱们须要又有开发环境的配置,又有生产环境的配置,甚至还要为命令增长其余的环境变量的时候,这个命令简直是又臭又长,咱们总不能每次都输入这个繁琐的命令,其实咱们能够利用npm scripts,这里是一篇阮一峰大神对npm脚本的介绍
//package.json { "scripts": { "build": "webpack --config config/webpack.config.js", "dev": "webpack-dev-server --config config/webpack.config.js" } }
npm run build npm run dev
须要注意的是在配置build&dev脚本的时候,咱们并无用
npx
命令,实际上,在npm脚本中的命令,npm默认都是优先查找本项目下node_modules下是否存在对应的模块及命令,若是没找到,才会查找全局的命令
一般一个项目中,并非只有一个js文件,而entry默认支持多文件入口,修改output跟随入口文件名字命名便可,简单作以下修改:
module.exports = { entry: { index: path.resolve(base, 'src', 'index.js'), main: path.resolve(base, 'src', 'main.js') }, output: { filename: 'js/[name].js', path: path.resolve(base, 'dist') } }
同时建立一个简单的main.js进行测试,再次编译会发如今dist/js目录下存在编译后的index.js和main.js文件
webpack使人兴奋的一个特性就是模块化,易于扩展其功能。webpack支持大量的模块导入方法,好比ES6(import)、CommonJS(require)、AMD等规范,而且加入了一些自由方法,具体的能够看官方说明文档。
用通俗的语言描述就是,webpack经过某个入口,去匹配各类模块导入规范,发现一个模块就根据对应配置寻找对应的loader去处理,如此往复,直处处理完毕全部依赖。
使用起来就是在modules字段中的rules(老版本名字为loaders,为保证兼容性,仍是支持这个字段的)中配置test,去匹配文件(js、css、图片资源等等),而后把这个文件交给合适的loader处理便可,因此但凡新出的框架,若是用到了独特的语法功能,都会配套提供对应的loader工具
目前虽然浏览器对ES6新特性的支持度都很是高,但还是有部分场景下,咱们只能运行ES5的代码,这时候就须要利用到js转码届的特斯拉Bebel及其插件了
yarn add babel-loader babel-core babel-preset-env
module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['env'] } } } ] },
显然test
中匹配了全部的js文件,exclude
字段去除了项目中依赖库里的文件,use
则是配置对应的loader。其中对于options,是做为参数传递给babel-loader的,babel的相关参数能够参考babel的官方网站,其中presets做为最主要的参数,告诉babel按照那种规则去解析代码,固然env
是一个组合,包含es201五、es2016等,当presets参数包含多个值时,babel的处理规则是倒序的,"es2017","es2016"
,babel会先去匹配es2016
的规则。另外,对于babel的配置,也能够经过在根目录创建.babelrc
方式去配置:
//.babelrc { "presets": [ "env" ] }
固然除了扩展js的语法,有时候咱们还须要扩展js的功能,好比在某些低版本的浏览器上运行'Hello World'.includes('Hello')
,这可使用babel-polyfill
这个组件,点击这里能够了解其使用方法。
固然,咱们能够只让webpack处理js文件,继续在html文件中经过link
标签引入css文件。但显然webpack但愿前端攻城狮们也能像模块化编写js同样,进行css的编写。咱们在src下建立css目录,并建立main.css文件
/* src/css/main.css */ body{ background-color: deepskyblue; /* 我喜欢填空的蓝色 */ }
//src/index.js import style from './css/main.css'
这时候使用npm run build
会发现编译报错,这是由于webpack没法处理载入main.css后的代码
ERROR in ./src/css/main.css Module parse failed: Unexpected token (1:4) You may need an appropriate loader to handle this file type. | body{ | background-color: red; | } @ ./src/index.js 3:12-37
webpack官方提供css-loader
专门处理导入的css模块,固然css-loader
也仅仅是完成了模块的导入处理,使webpack在编译时候再也不报错,实际上,还须要style-loader
处理导入后的css数据,自动添加到html的style标签中。若是你在测试过程当中,仅仅添加css-loader
loader,你会发现body实际上并无变成背景蓝
yarn add style-loader css-loader
//config webpack.config.js module->rules { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } } ] }
固然css-loader
还有一个重要配置选项,modules
参数,它支持导入的css中的类名自动重命名,这样即便在不一样组件使用了相同的css类命名,经处理后互相之间也不会出现影响。看下效果:
比较常见的预编译工具也就是Sass、Less、Stylus。在使用这三个预编译工具前,须要安装其对应的专属处理程序,好比Sass的处理工具node-sass。为了配合与webpack使用,还要安装对应的loader,好比sass-loader
yarn add sass-loader node-sass --dev
在webpack.config.js中添加对scss(Sass 3引入的新的语法格式,推荐新项目都用此)文件的支持,再起强调,webpack中loader执行顺序是从右往左,从下往上。
// webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } }, { loader: "sass-loader" } }] }] } };
测试一下:
/* src/sass/main.scss */ body { background-color: blue; } .main { background-color: grey }
//src/index.js // import style from './css/main.css' import style from './sass/main.scss' let es6 = () => { console.log('run in es6') console.log(style) } es6();
其余两种预编译器能够参考各自官方文档:less-loader、stylus-loader
PostCSS官网介绍是,利用js转译css的一个工具。对PostCSS的介绍,我比较承认这篇文章,PostCSS提供了一个解析器,把css转换为抽象语法树(AST),固然这个AST是可以被js处理的,而后交给各类插件处理后,再将AST转为css代码,因此关键是这些插件能完成哪些功能。
Autoprefixer 是一个流行的 PostCSS 插件,其做用是为 CSS 中的属性添加浏览器特定的前缀。
cssnext 插件容许开发人员在当前的项目中使用 CSS 未来版本中可能会加入的新特性。须要注意这个插件自己包含了Autoprefixer功能,因此若是使用了这个插件,则不须要Autoprefixer插件。
固然PostCSS中其实也包含支持Sass、Less等的插件,这个看我的喜爱,不过我本人却是蛮期待尝试下cssnext,毕竟大部分特性可能就是将来css支持的特性,提早熟悉下也算是预习。
更多插件能够读这篇文章,再也不叙述
安装postcss-loader及其插件autoprefixer
yarn add postcss-loader autoprefixer --dev
在webpack.config.js
中添加postcss支持,注意postcss处理css文件的位置
// webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]', minimize: false } }, { loader: 'postcss-loader', options: { config: { path: path.resolve(base, 'config', 'postcss.config.js') } } }, { loader: "sass-loader" }] }] } };
如上面配置,postcss须要单独的配置文件,建立config/postcss.config.js
,添加以下配置:
//config/postcss.config.js module.exports = { plugins: { 'autoprefixer': {} } }
file-loader提供了对图片资源的loader功能,而且利用 publicPath
选项还能很好的支持cdn,配合url-loader还能对小图片直接进行数字序列化(DataURL),减小网络请求,提升加载速度。
yarn add url-loader file-loader --dev
增长相关配置;url-loader的默认 fallback
loader就是 file-loader
为了更直观就写了出来,传递给 file-loader
的参数也只须要写在options中便可。这样background-image: url('../image/logo.jpg')
当咱们在css文件中使用这种方式引入图片时,就会触发url-loader去处理
//config/webpack.config.js module.exports = { ... module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192, fallback: 'file-loader', name: '[hash:5].[ext]', // publicPath: 'https://cdn.j2do.com/', outputPath: 'images/' } } ] } ] } }
Loader是专一于处理Webpack导入的资源模块,而插件是对Webpack功能的扩展。除了Webpack内置的插件,开发社区提供了大量优秀的插件。固然插件也是解决问题的,咱们仍是以问题为导向,去介绍几款插件。
extract-text-webpack-plugin
分离css到独立文件yarn add extract-text-webpack-plugin --dev
其中ExtractTextPlugin中fallback是指定了若是不须要提取到独立css文件中的样式文件,则交给 style-loade
处理,其余loader配置,跟以前没有差别。一样若是是预编译文件(Sass、Less等)的话,也只须要增长对应的loader便可
module.exports = { ... module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ { loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' } }, { loader: 'postcss-loader', options: { config: { path: path.resolve(base, 'config', 'postcss.config.js') } } } ] }) } ] } ... plugins: [ new ExtractTextPlugin("css/[name].css") ] };
再次运行 yarn run build
会发现生成了独立的css文件
html-webpack-plugin
自动生成 index.html
等入口文件随着多入口以及css的分离,手动去写html的入口文件也是一件麻烦事,这时候 html-webpack-plugin
就排上用途了, html-webpack-plugin
可以根据某个模板文件自动生成入口的html,包括多个入口,安装及配置以下:
yarn add html-webpack-plugin --dev
module.exports = { ... plugins: [ new ExtractTextPlugin("[name].css"), new HtmlWebpackPlugin({ title: 'Index Page', template: path.resolve(base, 'src', 'template/index.html.tmpl'), filename: "index.html", chunks: ['index'] }), new HtmlWebpackPlugin({ title: 'Main Page', template: path.resolve(base, 'src', 'template/index.html.tmpl'), filename: "main.html", chunks: ['main'] }), ] };
建立模板文件,注意之因此命名为 .tmpl
是为了防止 .html 可能会被loader解析,<%= ... %>
将不会被插件识别,完成变量替换。以下图,咱们的 title
是以变量形式在 webpack.config.js
中配置。该插件还支持多种模板文件,具体可见官方文档
<!-- src/template/index.html.tmpl --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> <%= htmlWebpackPlugin.options.title %> </title> </head> <body> </body> </html>
前面已经将js、css等分离到单独文件,接下来就是优化压缩这些代码。
对于css而言,只须要配置 css-loader
的 minimize
参数为 true
便可;固然还能够利用postcss的 cssnano
插件进行代码的压缩和优化,听说 cssnano
是目前css压缩优化中效果最好的工具。
对于js的压缩,咱们借助于 uglifyjs-webpack-plugin
插件
yarn add uglifyjs-webpack-plugin --dev
module.exports = { ... plugins: [ new ExtractTextPlugin("css/[name].css"), new HtmlWebpackPlugin({ title: 'Index Page', template: path.resolve(base, 'src', 'template/index.html.tmpl'), filename: "index.html", chunks: ['index'] }), new UglifyJsPlugin() ] };
Eslint再也不介绍,在webpack下使用Eslint须要以下依赖包:
yarn add eslint eslint-loader babel-eslint eslint-config-standard --dev
其中eslint是必须的,eslint-loader
是链接eslint与webpack的loader,babel-eslint
是一个eslint解析器,使其能支持es6等语法检测,eslint-config-standard
是Airbnb的规范配置,目前最流行的js规范。
安装过程当中若是有提示eslint-config-standard@11.0.0-beta.0" has unmet peer dependency "eslint-plugin-import@>=2.2.0"
等等,直接安装对应的包便可,好比:yarn add eslint-plugin-import --dev
便可
eslint是规范js语法,因此他须要处理的是js文件,并且应该是先于全部loader去处理js文件,若是出错或者不规范则纠正之,这里能够利用webpack的enforce
属性,设置eslint检查,先于其余loader
module.exports = { ... module: { rules: [ { enforce: "pre", test: /\.js$/, exclude: /node_modules/,//注意不要检测node_modules里面的代码 loader: "eslint-loader", options: { fix: true //自动修复不规范的代码,并非全部代码都能自动修复的,一些缩进,引号等能直接处理 } } ... ] } }
同时还要为eslint增长对应的配置文件 .eslintrc
{ "parser": "babel-eslint", "extends": "standard", "rules": {} }
这时候再尝试build吧,会发现一些不规范的代码被自动修复,固然有些不规范的代码,没法自动修复的,会直接致使错误;好比,声明了一个函数,却没有使用!
未完待续~~(近几天更新)