webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)
当 webpack 处理应用程序时, 它会递归地构建一个依赖关系图(dependency graph), 其中包含应用程序需要的每个模块, 然后将所有这些模块打包成一个或多个 bundle
基本安装
局部安装, 官方也是推荐安装到项目目录下
- mkdir webpack-demo-1
- cd webpack-demo-1
- npm init -y // 生成 package.json, 并且一路同意, 如果没啥个性化的内容则省了你一路狂按 enter
- npm install --save-dev webpack // 安装到开发环境里面(devDependicies)
webpack 基本配置文件
- touch webpack.config.js
- vi webpack.config.js
配置文件内容如下
- const path = require('path');
- module.exports = {
- entry: './src/index.js', // 这里应用程序开始执行, webpack 开始打包
- output: {
- // webpack 如何输出结果的相关选项
- filename: 'bundle.js',// 输出资源块的名字(asset chunk)
- path: path.resolve(__dirname, 'dist') // 所有输出文件的目标路径, 我的就是./dist/bundle.js
- }
- };
把当前目录的 src 下的 index.js 打包到了 dist 目录下, 并且生成了 (emmited) 一个改头换面的 bundle.js, 里面的代码面目全非啊
可以有多个入口(entry)
- entry: {
- sCSS: './src/css/main.scss', // 对象的键名 scss 就是输出文件的 name
- bundle: './src/js/app.js'
- },
- output: {
- filename: "[name].js",
- path: path.resolve(__dirname, 'dist/js')
- // publicPath: "/output/"
- },
多个入口最好写成对象的形式, 官网说如果写成了数组, 输出的内容会是数组的第一个
上述代码会在 dist/js 目录下生成
scss.js 和 bundle.js
path
其中, 配置文件的第一行代码使用了 Node 的内置模块 path, 并且在它前面加上 __dirname 这个全局变量 (也就是第七行代码) 可以防止不同操作系统之间的文件路径问题, 并且可以使相对路径按照预期工作 即使你的 index.js 内容为空, bundle.js 里面也有一些基本的打包代码
基本的使用
- // 第一种方法, 使用当前目录的 node_modules 里面的 webpack
- ./node_modules/.bin/webpack
- // 第二种方法使用 npm 脚本
- // 首先在你的 package.json 里面添加下列代码
- {
- ...
- "scripts": {
- "build": "webpack"
- },
- ...
- }
- // 然后, 使用下列代码即可
- npm run bulid
- // 第三种方法, 高版本的 npm 自带了 npx
- npx webpack //npx 会自动查找当前依赖包中的可执行文件, 如果找不到, 就会去 PATH 里找如果依然找不到, 就会帮你安装!
所以说呢, 我选择了第三种使用方法
babel-loader
ES6 或其他版本 js 转换成通用 js 代码, 毫无疑问应该使用 babel, 不过在 webpack 的世界里面统一使用 loader, 所以我们
google webpack babel-loader
loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块, 然后你就可以利用 webpack 的打包能力, 对它们进行处理
有个坑, 很容易搞错的坑
babel-loader 不同版本的安装脚本配置文件是不同的
(@ο@) 哇~你搜出的最新的文档用这么小的文字告诉你, 你用
webpack 3.x babel-loader 7.x | babel 6.x
的去这个链接,
webpack 3.x | babel-loader 8.x | babel 7.x
的去当前的这链接链接 好吧, 我用上一版本的吧 所以我的安装脚本是
npm install --save-dev babel-loader babel-core babel-preset-env webpack
配置文件是
- // 依然属于 webpack.config.js 配置的一部分,
- module: {
- // 这是关于模块的配置
- rules: [
- // 模块规则(配置 loader 解析器等选项)
- {
- test: /\.js$/, // 使用正则判断后缀是 js 的文件
- exclude: /(node_modules|bower_components)/,
- // 除了这两目录下的 node_modules|bower_components
- use: {
- loader: 'babel-loader', // 用这个 loader 处理. js 的文件
- options: {
- presets: ['env'] // 选项, 还记得单独使用 babel 的时候建立的那个. babelrc 嘛, 就是那个作用
- }
- }
- }
- ]
- }
借此可以得到 loader 的两个作用:
识别出应该被对应的 loader 进行转换的那些文件(使用 test 属性)
转换这些文件, 从而使其能够被添加到依赖中(并且最终添加到 bundle 中)(use 属性) 在./src/js / 有
module-1.jsmodule-2.jsapp.js
三个文件, 都是新的语法, 用的模块化写法, 有的浏览器不支持, 所以需要转化
- //module-1.js 代码
- function fn(){
- console.log(1)
- }
- export default fn
- //module-2.js
- function fn(){
- console.log(2)
- }
- export default fn
- //app.js
- import x from './module-1.js'
- import y from './module-2.js'
- x()
- y()
最终效果, 打开的我的预览链接, 使用 ctrl+shift+J, 会看到打印出 1 和 2
sass-loader
如果使用了预编译的 scss 语言, 要把 scss 文件变成 css 并加入到 html 里面, 思路同上,
google webpack scss
得到如下代码
npm install sass-loader node-sass webpack --save-dev
模块配置文件
- // webpack.config.js
- module.exports = {
- ...
- module: {
- rules: [{
- test: /\.scss$/,
- use: [{
- loader: "style-loader" // creates style nodes from JS strings
- }, {
- loader: "css-loader" // translates CSS into CommonJS
- }, {
- loader: "sass-loader" // compiles Sass to CSS
- }]
- }]
- }
- };
这个官方的就比较给力了, 很清楚地用法
先用 sass-loader 把
./src/csa/main.scss
编译成 main.css
再用 css-loader 把 main.css 变成符合 CommonJS 规范的
把 main.css 变成 js 字符串, 并创建 style 节点, 把它放进去, 这样 html 就可以显示啦
不过坑爹依旧用的时候报错喽~
第一次就说缺 style-loader, 好吧自觉点把另一个一起安装了把
npm i --save-dev css-loader style-loader
所以, 打开我的预览链接, 会看到我的预览的背景是灰色的
此时的 webpack.config.js 最终代码
- const path = require('path');
- module.exports = {
- entry: './src/js/app.js',
- output: {
- filename: 'bundle.js',
- path: path.resolve(__dirname, 'dist/js/')
- },
- module: {
- rules: [
- {
- test: /\.js$/,
- exclude: /(node_modules|bower_components)/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ['env']
- }
- }
- },
- {
- test: /\.scss$/,
- use: [{
- loader: "style-loader" // creates style nodes from JS strings
- }, {
- loader: "css-loader" // translates CSS into CommonJS
- }, {
- loader: "sass-loader" // compiles Sass to CSS
- }]
- }
- ]
- }
- };
所以借助 webpack 强大的模块化, 通过其构建的依赖关系图 (dependency graph) 把 jsscss 都搞到了 bundle.js 里面, 真是牛~
html-loader
本来搞了一个 html-loader, 优化 html, 把空格注释都给压缩掉, 提高性能, 可是实际使用中也没有报错, 也没啥效果, 比较尴尬自己埋个坑后面补一补
Copy Webpack Plugin
目前呢, 前面的 loader 用的都很爽在 src 目录下修改完了代码, 一个 npx webpack, 刷新就可以看到效果了, 体验很棒 但是今天坐在电脑前面, 回想代码, 以前在前端工程话的道路上, scssjshtml 都是被监视着(wacth), 开四个命令行窗口, 只要 src 下一有风吹草动, 就会把修改后的代码更新过去
目前使用的 webpack 可以完全自动化 scssjs 了, 可我如果修改了 src/index.html,dist / 也无法获知我的修改啊
然后我 google 一一会, 发现了这货
Copy Webpack Plugin
哎呀, 是个 plugin, 终于 webpack 的四大基本概念都到齐了, 前面搞了 entry output loder, 今天用一下 plugin
loader 被用于转换某些类型的模块, 而插件则可以用于执行范围更广的任务插件的范围包括, 从打包优化和压缩, 一直到重新定义环境中的变量 基本安装
npm i -D copy-webpack-plugin
Copy Webpack Plugin 配置文件(plugin 的和 loader 的配置文件可不是一个套路 loader 是在 module.rules 数组的每一个对象里面(即 rules 数组的每一个 value), 而 plugin 是在 module 的 plugins 数组里面)
- // 依然在 webpack.config.js
- const CopyWebpackPlugin = require('copy-webpack-plugin')
- const config = {
- plugins: [
- new CopyWebpackPlugin([ ...patterns ], options)
- ]
- }
Copy Webpack Plugin 的 github 给的代码, 一开始把我搞蒙了, 和 webpack 官网的代码不大一样啊后来才发现原来用了
module.exports = config;
在我的小 demo 里使用的是
- plugins: [
- new CopyWebpackPlugin([ {
- // 原来一个 plugin 就是一个对象啊, 使用的时候实例化对象即可
- from: 'src/index.html', // 从 src/index.html 目录下复制
- to: '../index.html', // 到 dist/index.html
- toType: 'file' // 复制类型是文件
- }], { copyUnmodified: true }) // 把未修改的部分也复制过去
- ]
这个插件可以实现很多功能, 具体的细节看这里
关于目录的一个小问题
上面代码为什么这么写呢
to: '../index.html',
, 试了好几遍发现没有报错, 就是没有结果, 最后搞明白了是路径的问题 还记得 四大基本概念的 output 里面的 path 吗, 回头看一开始的 path
- output: {
- filename: 'bundle.js',
- path: path.resolve(__dirname, 'dist/js/')
- },
项目的 path 是 dist/js 下, 所以应该复制到上一级目录下../ 也就是 dist / 目录下了
可以看到预览链接里面的文字啦
动态效果可以看下图
postcss-loader
上述代码有个小问题使用了 display: flex 把 ul>li 变成了横排, 但是这玩意有兼容性当初我的一篇文章唯一的一个评论就是这么说我的
检查兼容性(虽然这是严谨要求, 我还是老忘), 可以去 caniuse 看一下,(@ο@) 哇~IE 没有绿的哎, 支持太差了( o )! 万一以后我项目搞大了, IE 的用户老安卓的用户想看我项目咋办呢, 只能加一下前缀优化一下啦 有个挺牛的在线的 autoprefixer, 也可以去在线转换 既然使用了 webpack 就
Google webpack autoprefixer
, 遗憾的发现 autoprefixer 官方推荐使用 postcss-loader
postcss-loader 解决兼容性问题
先吐槽一下, 这货的文档也是稀烂
官方安装脚本
npm i -D postcss-loader
需要单独配置文件 postcss.config.js, 官方的写法是下面这个(最无语的就是这个, 下面的必错, 写出来就是警告大家, 官方的也不一定对)
- module.exports = {
- parser: 'sugarss', // 铪???? 解析器是 sugarss???
- plugins: {
- 'postcss-import': {},
- 'postcss-cssnext': {},
- 'cssnano': {}
- }
- }
在 webpack.config.js 的添加时还要注意下面的几点
After setting up your postcss.config.js, add postcss-loader to your webpack.config.js. You can use it standalone or in conjunction with css-loader (recommended). Use it after css-loader and style-loader, but before other preprocessor loaders like e.g sass|less|stylus-loader, if you use any.
这段文档的要点就是让你注意 postcss-loader 应该在
css-loader style-loader
之后, 但是一定要在其他的预处理器
preprocessor loaders
之前, 例如
sass|less|stylus-loader
官方给了一个推荐的配置代码
- // 依然是 webpack.config.js
- module.exports = {
- module: {
- rules: [
- {
- test: /\.css$/,
- use: [
- 'style-loader',
- { loader: 'css-loader', options: { importLoaders: 1 } },
- 'postcss-loader'
- ]
- }
- ]
- }
- }
本项目用的是. src/css/main.scss, 只能尝试着将上述代码加到相应的位置
- rules: [
- ...
- {
- test: /\.scss$/,
- use: [{
- loader: "style-loader" // creates style nodes from JS strings
- }, {
- loader: "css-loader", options: { importLoaders: 1 }// translates CSS into CommonJS
- }, {
- loader: "postcss-loader"
- }, {
- loader: "sass-loader" // compiles Sass to CSS
- }]
- },
- ...
- ]
下面的几点可都是官网文档没写的, 只能自己踩一踩的坑
运行 npx webpack, 连续报错, 不过是缺必备的 module 的错误, 也就是缺 postcss.config.js 里面的
postcss-import postcss-cssnext cssnano sugarss
没办法, 先
npm i -D 上面的四个模块名字
, 依然报错, 这次是语法错误
(v)嗯??? 它说我不必要的大括号??? 我这标准的 scss 语法啊, 又不是 sass 的语法(它省略了大括号和分号), 先 Google 一波这个错误 终于在在 postcss 的 issue 里面发现了蛛丝马迹, 问题果然出在那个令我疑惑的 postcss.config.js 里面
错误原因分析
错误的使用了 sugarss 的解析器(这货和 sass 类似, 没有大括号, 所以它说我大括号错了, 它的特点是 Indent-based CSS syntax for PostCSS.SugarSS MIME-type is text/x-sugarss with .sss file extension.), 而我写的是 scss 语法
postcss-loader 哪来的勇气确定大家都是用的. sss 后缀的 sugarss 语法呢, 还敢直接在文档的醒目位置推荐
稀烂的 postcss.config.js
,O__O "
那么多的预编译的 css 语法, 果然需要 webpack 打包工具啊, 找到合适的 loader 去解析啊
注释掉 parser: 'sugarss', 这句代码, 可以使用默认的解析器去解析了, 正常运行了 不过查看代码, 发现好像转换后的 css 有点小丑
autoprefixer 冗余
仔细观察命令行, 发现有线索, 一个警告
警告信息提示我说: postcss-cssnext 发现有个冗余的 autoprefixer 插件在我的 postcss 插件里面, 这个可能有不良影响, 我应该移除它, 因为它已经包括在了 postcss-cssnext 里面
webpack 的警告说的很明白, postcss-cssnext 是无辜的, 而且我确定按照官网代码走的, 没有安装 autoprefixer 插件, 错误必然在剩下的两个插件里面了
- // 修改后的 postcss.config.js 只剩下这些了
- module.exports = {
- plugins: {
- 'postcss-import': {}, //1. 它错了?
- 'postcss-cssnext': {}, //webpack 告诉我它是清白的
- 'cssnano': {} //2. 它错了?
- }
- }
我选择了排除法:
先注释
'postcss-import': {},
, 发现无法转换后的 css 代码不对, 说明它是无辜的
那么问题必然是最后一个插件, 注释掉'cssnano': {}, 终于完美了, 而且代码很优美
本着打破砂锅问到底的精神, 我搜了一下 cssnano, 在其官网看到了真实的错误原因, webpack 很明智啊, 诚不欺我, 果然冗余插件了
cssnano 里面有 autoprefixer 导致了冗余
extract-text-webpack-plugin
webpack 把所有的资源都当成了一个模块, CSSJS 文件 都是资源, 都可以打包到一个 bundle.js 文件中. 但是有时候需要把样式 单独的打包成一个文件需要抽离出 css 文件到单独的 css / 下
使用 extract-text-webpack-plugin 插件可以做到
安装脚本
npm install extract-text-webpack-plugin --save-dev
配置 webpack.config.js, 官方推荐的配置如下, 但是需要结合自己的项目修改一下
- const ExtractTextPlugin = require("extract-text-webpack-plugin"); // 插件的套路都要 require 进来
- module.exports = {
- module: {
- rules: [
- {
- test: /\.css$/,
- use: ExtractTextPlugin.extract({
- fallback: "style-loader", // 失败了就用它解析
- use: "css-loader" // 是 css 文件, 就用这个处理
- })
- }
- ]
- },
- plugins: [
- new ExtractTextPlugin("styles.css"),
- ]
- }
目前的问题
我如果使用的是 scss, 就很尴尬了
最开始由一个错误引起
Google 一下, 解决掉 error,fallbak 里面使用了 style-loader,use 里面不应该使用了, 那么问题又来了
问题一: 无法使用 style-loader 把我的 main.scss 转化后的 css 代码插入到 style 标签里面
详见代码注释部分
- {
- test: /\.scss$/,
- use: ExtractTextPlugin.extract({
- fallback: 'style-loader',
- // 如果在 use 里面写上 style-loader, 就报错 window 未被定义, 可是不写的话, 我的 bundle.js 里面就无法把 css 放到 style 标签里面, 只能手动把分离的 css 加到 index.html, 很无语
- use: [{
- loader: "css-loader", options: { importLoaders: 1 }// translates CSS into CommonJS
- }, {
- loader: "postcss-loader"
- }, {
- loader: "sass-loader" // compiles Sass to CSS
- }]
- })
- }
问题 2: 可以提取出单独的 css 文件, 但是没想到如何放到单独的 css / 下, 很尴尬, 代码未提交, 还在摸索中
问题大约知道应该出现在下面这个函数里面
- new ExtractTextPlugin({
- filename: (getPath) => {
- return getPath('../main.css').replace('../js', '../css'); // 本意是生成在 dist/css/main.css, 结果只是在 dist/main.css 目录下, 没有 css/
- },
- allChunks: true
- })
所有的代码都在我的 demo 里面
蛋疼的无力吐槽
算是搞定了 webpack 的基本使用了吧, 最简单的符合我目前技术栈的各种 loader,plugin 都会安装了 当然, 还有无数的 webpack 的 loaderplugin 在前方等着我去探索各种稀奇古怪的配置文件痛并快乐着
五花八门的配置文件挺让我糟心的幸亏有了 node 爸爸帮我啊, webpack 爸爸虽然也是比较严厉的, 但是省了你用四个命令行窗口的啊, 还是很感人的啊
工具这个东西嘛
配置出错了怎么办, 默念三句
如果真的搞蹦了怎么办熊得, 送你一句名言
没有什么 bug 不是一遍 webpack 解决不了额, 如果有的话, 那就来三遍 webpaack 总有一天让 webpack 叫你爸爸!!!
新的挑战者 parcel
现在的吐槽大概是没有经历过以前更蛋疼的日子吧, 幸好有了新的后起之秀 ---parcel, 它的官网老厉害了, 智能提示我用了中文, 真是贴心
回顾一下 webpack 的首页
在对比一下 parcel 的首页
两者的目的是一样的, 不过 parcel 不需要插件, 而且速度快
快速开始是真的快
快速开始
没有配置, 最好以 html 或者 js 为入口, 直接
npm init -y , parcel index.html
, 可以实现 index.js
它会自动帮你打包到 dist 目录下的一个 js 文件里面, 并复制 index.html 过去, 而这一切只需要上面的一行代码
一开始我的项目的目录结构
执行 parcel index.html 的目录结构
而我当时搞 webpack 的时候的快速开始至少需要安装 webpackwebpack.config.js 修改配置内容安装插件才能实现上述的功能
模块化和 scss 的解析很方便
当我在 parcel-demo 目录下使用 parcel index.html 的时候, 它自动发现我引入了 index.js
- <body>
- <script src="./index.js"></script>
- </body>
里面的内容是模块化的内容, 它自动帮我转成了
dist/parcel-demo.js
, 我在 webpack 的时候需要 babel-loader
我用的是 scss, 它也会自动发现, 并且竟然
还帮我自动下载了 node-sass
其他的特点都在官网去发掘吧~希望日后 parcel 快速崛起吧
而现在我还是要用 webpack 的
来源: https://juejin.im/post/5a82914ff265da4e8077b5c6